diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..063b0e4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +.DS_Store +Thumbs.db +db.json +*.log +node_modules/ +public/ +.deploy*/ \ No newline at end of file diff --git "a/2020/01/22/linux \345\206\205\346\240\270RingBuffer\345\256\236\347\216\260/index.html" "b/2020/01/22/linux \345\206\205\346\240\270RingBuffer\345\256\236\347\216\260/index.html" deleted file mode 100644 index 7573370..0000000 --- "a/2020/01/22/linux \345\206\205\346\240\270RingBuffer\345\256\236\347\216\260/index.html" +++ /dev/null @@ -1,443 +0,0 @@ - - - - - - - -linux 内核RingBuffer实现 - Genge 随笔 - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - -
-
-
-

linux 内核RingBuffer实现

- -
-
-

实现方式非常巧妙,刚开始看的有点奇怪,当发现实现原理后惊讶了一番..

-
/**@brief 仿照linux kfifo写的ring buffer
-* ring_buffer.h
- * */
- 
-#ifndef KFIFO_HEADER_H 
-#define KFIFO_HEADER_H
- 
-#include <inttypes.h>
-#include <string.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <errno.h>
-#include <assert.h>
- 
-//判断x是否是2的次方
-#define is_power_of_2(x) ((x) != 0 && (((x) & ((x) - 1)) == 0))
-//取a和b中最小值
-#define min(a, b) (((a) < (b)) ? (a) : (b))
- 
-struct ring_buffer
-{
-    void         *buffer;     //缓冲区
-    uint32_t     size;       //大小
-    uint32_t     in;         //入口位置
-    uint32_t       out;        //出口位置
-    pthread_mutex_t *f_lock;    //互斥锁
-};
-//初始化缓冲区
-struct ring_buffer* ring_buffer_init(void *buffer, uint32_t size, pthread_mutex_t *f_lock)
-{
-    assert(buffer);
-    struct ring_buffer *ring_buf = NULL;
-    if (!is_power_of_2(size))
-    {
-    fprintf(stderr,"size must be power of 2.\n");
-        return ring_buf;
-    }
-    ring_buf = (struct ring_buffer *)malloc(sizeof(struct ring_buffer));
-    if (!ring_buf)
-    {
-        fprintf(stderr,"Failed to malloc memory,errno:%u,reason:%s",
-            errno, strerror(errno));
-        return ring_buf;
-    }
-    memset(ring_buf, 0, sizeof(struct ring_buffer));
-    ring_buf->buffer = buffer;
-    ring_buf->size = size;
-    ring_buf->in = 0;
-    ring_buf->out = 0;
-        ring_buf->f_lock = f_lock;
-    return ring_buf;
-}
-//释放缓冲区
-void ring_buffer_free(struct ring_buffer *ring_buf)
-{
-    if (ring_buf)
-    {
-    if (ring_buf->buffer)
-    {
-        free(ring_buf->buffer);
-        ring_buf->buffer = NULL;
-    }
-    free(ring_buf);
-    ring_buf = NULL;
-    }
-}
- 
-//缓冲区的长度
-uint32_t __ring_buffer_len(const struct ring_buffer *ring_buf)
-{
-    return (ring_buf->in - ring_buf->out);
-}
- 
-//从缓冲区中取数据
-uint32_t __ring_buffer_get(struct ring_buffer *ring_buf, void * buffer, uint32_t size)
-{
-    assert(ring_buf || buffer);
-    uint32_t len = 0;
-    size  = min(size, ring_buf->in - ring_buf->out);        
-    /* first get the data from fifo->out until the end of the buffer */
-    len = min(size, ring_buf->size - (ring_buf->out & (ring_buf->size - 1)));
-    memcpy(buffer, ring_buf->buffer + (ring_buf->out & (ring_buf->size - 1)), len);
-    /* then get the rest (if any) from the beginning of the buffer */
-    memcpy(buffer + len, ring_buf->buffer, size - len);
-    ring_buf->out += size;
-    return size;
-}
-//向缓冲区中存放数据
-uint32_t __ring_buffer_put(struct ring_buffer *ring_buf, void *buffer, uint32_t size)
-{
-    assert(ring_buf || buffer);
-    uint32_t len = 0;
-    size = min(size, ring_buf->size - ring_buf->in + ring_buf->out);
-    /* first put the data starting from fifo->in to buffer end */
-    len  = min(size, ring_buf->size - (ring_buf->in & (ring_buf->size - 1)));
-    memcpy(ring_buf->buffer + (ring_buf->in & (ring_buf->size - 1)), buffer, len);
-    /* then put the rest (if any) at the beginning of the buffer */
-    memcpy(ring_buf->buffer, buffer + len, size - len);
-    ring_buf->in += size;
-    return size;
-}
- 
-uint32_t ring_buffer_len(const struct ring_buffer *ring_buf)
-{
-    uint32_t len = 0;
-    pthread_mutex_lock(ring_buf->f_lock);
-    len = __ring_buffer_len(ring_buf);
-    pthread_mutex_unlock(ring_buf->f_lock);
-    return len;
-}
- 
-uint32_t ring_buffer_get(struct ring_buffer *ring_buf, void *buffer, uint32_t size)
-{
-    uint32_t ret;
-    pthread_mutex_lock(ring_buf->f_lock);
-    ret = __ring_buffer_get(ring_buf, buffer, size);
-    //buffer中没有数据
-    if (ring_buf->in == ring_buf->out)
-    ring_buf->in = ring_buf->out = 0;
-    pthread_mutex_unlock(ring_buf->f_lock);
-    return ret;
-}
- 
-uint32_t ring_buffer_put(struct ring_buffer *ring_buf, void *buffer, uint32_t size)
-{
-    uint32_t ret;
-    pthread_mutex_lock(ring_buf->f_lock);
-    ret = __ring_buffer_put(ring_buf, buffer, size);
-    pthread_mutex_unlock(ring_buf->f_lock);
-    return ret;
-}
-#endif
-
- -

使用栗子,采用多线程模拟生产者和消费者编写测试程序,如下所示:

-
/**@brief ring buffer测试程序,创建两个线程,一个生产者,一个消费者。
- * 生产者每隔1秒向buffer中投入数据,消费者每隔2秒去取数据。
- * */
-#include "ring_buffer.h"
-#include <pthread.h>
-#include <time.h>
- 
-#define BUFFER_SIZE  1024 * 1024
- 
-typedef struct student_info
-{
-    uint64_t stu_id;
-    uint32_t age;
-    uint32_t score;
-}student_info;
- 
- 
-void print_student_info(const student_info *stu_info)
-{
-    assert(stu_info);
-    printf("id:%lu\t",stu_info->stu_id);
-    printf("age:%u\t",stu_info->age);
-    printf("score:%u\n",stu_info->score);
-}
- 
-student_info * get_student_info(time_t timer)
-{
-    student_info *stu_info = (student_info *)malloc(sizeof(student_info));
-    if (!stu_info)
-    {
-    fprintf(stderr, "Failed to malloc memory.\n");
-    return NULL;
-    }
-    srand(timer);
-    stu_info->stu_id = 10000 + rand() % 9999;
-    stu_info->age = rand() % 30;
-    stu_info->score = rand() % 101;
-    print_student_info(stu_info);
-    return stu_info;
-}
- 
-void * consumer_proc(void *arg)
-{
-    struct ring_buffer *ring_buf = (struct ring_buffer *)arg;
-    student_info stu_info; 
-    while(1)
-    {
-    sleep(2);
-    printf("------------------------------------------\n");
-    printf("get a student info from ring buffer.\n");
-    ring_buffer_get(ring_buf, (void *)&stu_info, sizeof(student_info));
-    printf("ring buffer length: %u\n", ring_buffer_len(ring_buf));
-    print_student_info(&stu_info);
-    printf("------------------------------------------\n");
-    }
-    return (void *)ring_buf;
-}
- 
-void * producer_proc(void *arg)
-{
-    time_t cur_time;
-    struct ring_buffer *ring_buf = (struct ring_buffer *)arg;
-    while(1)
-    {
-    time(&cur_time);
-    srand(cur_time);
-    int seed = rand() % 11111;
-    printf("******************************************\n");
-    student_info *stu_info = get_student_info(cur_time + seed);
-    printf("put a student info to ring buffer.\n");
-    ring_buffer_put(ring_buf, (void *)stu_info, sizeof(student_info));
-    printf("ring buffer length: %u\n", ring_buffer_len(ring_buf));
-    printf("******************************************\n");
-    sleep(1);
-    }
-    return (void *)ring_buf;
-}
- 
-int consumer_thread(void *arg)
-{
-    int err;
-    pthread_t tid;
-    err = pthread_create(&tid, NULL, consumer_proc, arg);
-    if (err != 0)
-    {
-    fprintf(stderr, "Failed to create consumer thread.errno:%u, reason:%s\n",
-        errno, strerror(errno));
-    return -1;
-    }
-    return tid;
-}
-int producer_thread(void *arg)
-{
-    int err;
-    pthread_t tid;
-    err = pthread_create(&tid, NULL, producer_proc, arg);
-    if (err != 0)
-    {
-    fprintf(stderr, "Failed to create consumer thread.errno:%u, reason:%s\n",
-        errno, strerror(errno));
-    return -1;
-    }
-    return tid;
-}
- 
- 
-int main()
-{
-    void * buffer = NULL;
-    uint32_t size = 0;
-    struct ring_buffer *ring_buf = NULL;
-    pthread_t consume_pid, produce_pid;
- 
-    pthread_mutex_t *f_lock = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t));
-    if (pthread_mutex_init(f_lock, NULL) != 0)
-    {
-    fprintf(stderr, "Failed init mutex,errno:%u,reason:%s\n",
-        errno, strerror(errno));
-    return -1;
-    }
-    buffer = (void *)malloc(BUFFER_SIZE);
-    if (!buffer)
-    {
-    fprintf(stderr, "Failed to malloc memory.\n");
-    return -1;
-    }
-    size = BUFFER_SIZE;
-    ring_buf = ring_buffer_init(buffer, size, f_lock);
-    if (!ring_buf)
-    {
-    fprintf(stderr, "Failed to init ring buffer.\n");
-    return -1;
-    }
-#if 0
-    student_info *stu_info = get_student_info(638946124);
-    ring_buffer_put(ring_buf, (void *)stu_info, sizeof(student_info));
-    stu_info = get_student_info(976686464);
-    ring_buffer_put(ring_buf, (void *)stu_info, sizeof(student_info));
-    ring_buffer_get(ring_buf, (void *)stu_info, sizeof(student_info));
-    print_student_info(stu_info);
-#endif
-    printf("multi thread test.......\n");
-    produce_pid  = producer_thread((void*)ring_buf);
-    consume_pid  = consumer_thread((void*)ring_buf);
-    pthread_join(produce_pid, NULL);
-    pthread_join(consume_pid, NULL);
-    ring_buffer_free(ring_buf);
-    free(f_lock);
-    return 0;
-}
-
- -

参考文献

linux内核数据结构之kfifo - Daleshi的技术随笔 - 博客园

- -
-
-
-
- -
- - - - -
- - - - - - -
- - - -
-
- - \ No newline at end of file diff --git "a/2020/01/23/Linux shell\350\257\255\350\250\200\342\200\224\342\200\224dash\345\222\214bash/index.html" "b/2020/01/23/Linux shell\350\257\255\350\250\200\342\200\224\342\200\224dash\345\222\214bash/index.html" deleted file mode 100644 index 83a03be..0000000 --- "a/2020/01/23/Linux shell\350\257\255\350\250\200\342\200\224\342\200\224dash\345\222\214bash/index.html" +++ /dev/null @@ -1,250 +0,0 @@ - - - - - - - -Linux shell语言——dash和bash - Genge 随笔 - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - -
-
-
-

Linux shell语言——dash和bash

- -
-
-

什么是bash ?

-

Bash(GNU Bourne-Again Shell)是许多Linux平台的内定Shell,事实上,还有许多传统UNIX上用的Shell,像tcsh、csh、ash、bsh、ksh等等。

-

GNU/Linux 操作系统中的 /bin/sh 本是 bash (Bourne-Again Shell) 的符号链接,但鉴于 bash 过于复杂,有人把 bash 从 NetBSD 移植到 Linux 并更名为 dash (Debian Almquist Shell),并建议将 /bin/sh 指向它,以获得更快的脚本执行速度。Dash Shell 比 Bash Shell 小的多,符合POSIX标准。

-

Debian和Ubuntu中,/bin/sh默认已经指向dash,这是一个不同于bash的shell,它主要是为了执行脚本而出现,而不是交互,它速度更快,但功能相比bash要少很多,语法严格遵守POSIX标准。

-

就是这个倒霉的dash解释器使得我按照bash语法写的shell 脚本不能运行。

-

要知道自己的/bin/sh指向何种解释器,可以用 ls /bin/sh -al 命令查看:

-
$ ls /bin/sh -al
-lrwxrwxrwx 1 root root 4 1116 15:33 /bin/sh -> bash
- -

以上结果就表示当前系统用的是dash解释器。

-

切换到bash的方式其实挺简单的,关键是一直没找出这个原因……

-

修改默认的sh,可以采用命令sudo dpkg-reconfigure dash

-

会出现一个图片状的配置菜单,选no就可以了

-

再次检查一下, ls /bin/sh -al 发现软链接指向/bin/bash

-

         lrwxrwxrwx 1 root root 4 11月 16 15:33 /bin/sh -> bash

-

注:dash 和 bash 语法上的主要的区别有:

-
    -
  1. 定义函数
  2. -
-

bash: function在bash中为关键字

-

dash: dash中没有function这个关键字

-
    -
  1. select var in list; do command; done
  2. -
-

bash:支持

-

dash:不支持, 替代方法:采用while+read+case来实现

-
    -
  1. echo {0..10}
  2. -
-

bash:支持{n..m}展开

-

dash:不支持,替代方法, 采用seq外部命令

-
    -
  1. here string
  2. -
-

bash:支持here string

-

dash:不支持, 替代方法:可采用here documents

-
    -
  1. -

    &word重定向标准输出和标准错误

    -
    -
  2. -
-

bash: 当word为非数字时,>&word变成重定向标准错误和标准输出到文件word

-

dash: >&word, word不支持非数字, 替代方法: >word 2>&1; 常见用法 >/dev/null 2>&1

-
    -
  1. 数组
  2. -
-

bash: 支持数组, bash4支持关联数组

-

dash: 不支持数组,替代方法, 采用变量名+序号来实现类似的效果

-
    -
  1. 子字符串扩展
  2. -
-

bash: 支持${parameter:offset:length},${parameter:offset}

-

dash: 不支持, 替代方法:采用expr或cut外部命令代替

-
    -
  1. 大小写转换
  2. -
-

bash: 支持${parameter^pattern},${parameter^^pattern},${parameter,pattern},${parameter,,pattern}

-

dash: 不支持,替代方法:采用tr/sed/awk等外部命令转换

-
    -
  1. 进程替换<(command), >(command)
  2. -
-

bash: 支持进程替换

-

dash: 不支持, 替代方法, 通过临时文件中转

-
    -
  1. [ string1 = string2 ] 和 [ string1 == string2 ]
  2. -
-

bash: 支持两者

-

dash: 只支持=

-
    -
  1. [[ 加强版test
  2. -
-

bash: 支持[[ ]], 可实现正则匹配等强大功能

-

dash: 不支持[[ ]], 替代方法,采用外部命令

-
    -
  1. for (( expr1 ; expr2 ; expr3 )) ; do list ; done
  2. -
-

bash: 支持C语言格式的for循环

-

dash: 不支持该格式的for, 替代方法,用while+$((expression))实现

-
    -
  1. let命令和((expression))
  2. -
-

bash: 有内置命令let, 也支持((expression))方式

-

dash: 不支持,替代方法,采用$((expression))或者外部命令做计算

-
    -
  1. $((expression))
  2. -
-

bash: 支持id++,id–,++id,–id这样到表达式

-

dash: 不支持++,–, 替代方法:id+=1,id-=1, id=id+1,id=id-1

- -
-
-
-
- -
- - - - - - -
- - - - - - -
- - - -
-
- - \ No newline at end of file diff --git "a/2020/01/23/TCP \344\270\211\346\254\241\346\217\241\346\211\213\345\222\214\345\233\233\346\254\241\346\214\245\346\211\213/index.html" "b/2020/01/23/TCP \344\270\211\346\254\241\346\217\241\346\211\213\345\222\214\345\233\233\346\254\241\346\214\245\346\211\213/index.html" deleted file mode 100644 index 7415fc4..0000000 --- "a/2020/01/23/TCP \344\270\211\346\254\241\346\217\241\346\211\213\345\222\214\345\233\233\346\254\241\346\214\245\346\211\213/index.html" +++ /dev/null @@ -1,219 +0,0 @@ - - - - - - - -TCP 三次握手和四次挥手 - Genge 随笔 - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - -
-
-
-

TCP 三次握手和四次挥手

- -
-
-

首先需要了解的知识

    -
  • TCP的包是没有IP地址的,那是IP层上的事。但是有源端口和目标端口。
  • -
  • 一个TCP连接需要四个元组来表示是同一个连接(src_ip, src_port, dst_ip, dst_port)准确说是五元组,还有一个是协议。但因为这里只是说TCP协议,所以,这里我只说四元组。
  • -
  • 注意上图中的四个非常重要的东西:
      -
    • Sequence Number是包的序号,用来解决网络包乱序(reordering)问题。(seq)
    • -
    • Acknowledgement Number就是ACK——用于确认收到,用来解决不丢包的问题。
    • -
    • Window又叫Advertised-Window,也就是著名的滑动窗口(Sliding Window),用于解决流控的。
    • -
    • TCP Flag ,也就是包的类型,主要是用于操控TCP的状态机的。主要有SYN、FIN、ACK等
    • -
    -
  • -
-

TCP的状态机

其实,网络上的传输是没有连接的,包括TCP也是一样的。而TCP所谓的“连接”,其实只不过是在通讯的双方维护一个“连接状态”,让它看上去好像有连接一样。所以,TCP的状态变换是非常重要的。
如图:(用excel画图的难受,又早不到其他好工具,能推荐个吗?)
TCP连接、通讯、断开流程图
图中Client 、Server并不是严格区分的,TCP是全双工的,双方都可以充当Server、Client

-
    -
  1. 三次握手

    -
      -
    1. 第一次握手:建立连接。客户端发送连接请求报文段,将SYN位置为1,Sequence Number为x;然后,客户端进入SYN_SEND状态,等待服务器的确认;
    2. -
    3. 第二次握手:服务器收到SYN报文段。服务器收到客户端的SYN报文段,需要对这个SYN报文段进行确认,设置Acknowledgment Number为x+1(Sequence Number+1);同时,自己自己还要发送SYN请求信息,将SYN位置为1,Sequence Number为y;服务器端将上述所有信息放到一个报文段(即SYN+ACK报文段)中,一并发送给客户端,此时服务器进入SYN_RECV状态;
    4. -
    5. 第三次握手:客户端收到服务器的SYN+ACK报文段。然后将Acknowledgment Number设置为y+1,向服务器发送ACK报文段,这个报文段发送完毕以后,客户端和服务器端都进入ESTABLISHED状态,完成TCP三次握手。
      完成了三次握手,客户端和服务器端就可以开始传送数据。以上就是TCP三次握手的总体介绍。
    6. -
    -
  2. -
  3. 四次挥手
    当客户端和服务器通过三次握手建立了TCP连接以后,当数据传送完毕,肯定是要断开TCP连接的啊。那对于TCP的断开连接,这里就有了神秘的“四次分手”。

    -
      -
    1. 第一次分手:主机1(可以使客户端,也可以是服务器端),设置Sequence Number和Acknowledgment Number,向主机2发送一个FIN报文段;此时,主机1进入FIN_WAIT_1状态;这表示主机1(client)没有数据要发送给主机2(server)了
    2. -
    3. 第二次分手:主机2收到了主机1发送的FIN报文段,向主机1回一个ACK报文段,Acknowledgment Number为Sequence Number加1;主机1进入FIN_WAIT_2状态;主机2告诉主机1,我“同意”你的关闭请求;
    4. -
    5. 第三次分手:主机2向主机1发送FIN报文段,请求关闭连接,同时主机2进入LAST_ACK状态;
    6. -
    7. 第四次分手:主机1收到主机2发送的FIN报文段,向主机2发送ACK报文段,然后主机1进入TIME_WAIT状态;主机2收到主机1的ACK报文段以后,就关闭连接;此时,主机1等待2MSL后依然没有收到回复,则证明Server端已正常关闭,那好,主机1也可以关闭连接了。
    8. -
    -
  4. -
-

至此,TCP的四次分手就这么愉快的完成了。当你看到这里,你的脑子里会有很多的疑问,很多的不懂,感觉很凌乱;没事,我们继续总结。

-
    -
  1. 三次握手和四次挥手意义
    对于建链接的3次握手,主要是要初始化Sequence Number 的初始值。通信的双方要互相通知对方自己的初始化的Sequence Number(缩写为ISN:Inital Sequence Number)——所以叫SYN,全称Synchronize Sequence Numbers。也就上图中的 x 和 y。这个号要作为以后的数据通信的序号,以保证应用层接收到的数据不会因为网络上的传输的问题而乱序(TCP会用这个序号来拼接数据)。
    对于四次挥手,其实仔细看是2次,因为TCP是全双工的,所以,发送方和接收方都需要Fin和Ack。只不过,有一方是被动的,所以看上去就成了所谓的4次挥手。如果两边同时断连接,那就会就进入到CLOSING状态,然后到达TIME_WAIT状态。
  2. -
-

为什么要三次握手

-
-

在谢希仁著《计算机网络》第四版中讲“三次握手”的目的是“为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误”。在另一部经典的《计算机网络》一书中讲“三次握手”的目的是为了解决“网络中存在延迟的重复分组”的问题。

-
-

在谢希仁著《计算机网络》书中同时举了一个例子,如下:

-
-

“已失效的连接请求报文段”的产生在这样一种情况下:client发出的第一个连接请求报文段并没有丢失,而是在某个网络结点长时间的滞留了,以致延误到连接释放以后的某个时间才到达server。本来这是一个早已失效的报文段。但server收到此失效的连接请求报文段后,就误认为是client再次发出的一个新的连接请求。于是就向client发出确认报文段,同意建立连接。假设不采用“三次握手”,那么只要server发出确认,新的连接就建立了。由于现在client并没有发出建立连接的请求,因此不会理睬server的确认,也不会向server发送数据。但server却以为新的运输连接已经建立,并一直等待client发来数据。这样,server的很多资源就白白浪费掉了。采用“三次握手”的办法可以防止上述现象发生。例如刚才那种情况,client不会向server的确认发出确认。server由于收不到确认,就知道client并没有要求建立连接。”
计算机网络三次握手

-
-

一句话总结就是:防止了服务器端的一直等待而浪费资源。

-

为什么要四次挥手
TCP协议是一种面向连接的、可靠的、基于字节流的运输层通信协议。TCP是全双工模式,这就意味着,当主机1发出FIN报文段时,只是表示主机1已经没有数据要发送了,主机1告诉主机2,它的数据已经全部发送完毕了;但是,这个时候主机1还是可以接受来自主机2的数据;当主机2返回ACK报文段时,表示它已经知道主机1没有数据发送了,但是主机2还是可以发送数据到主机1的;当主机2也发送了FIN报文段时,这个时候就表示主机2也没有数据要发送了,就会告诉主机1,我也没有数据要发送了,之后彼此就会愉快的中断这次TCP连接。

-

为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSED状态?
虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSED状态了,但是我们必须假象网络是不可靠的,有可能最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。

-

状态机状态解释

    -
  • CLOSED: 这个没什么好说的了,表示初始状态,也是最后状态。
  • -
  • LISTEN: 这个也是非常容易理解的一个状态,表示服务器端的某个SOCKET处于监听状态,可以接受连接了。
  • -
  • SYN_RCVD: 这个状态表示接受到了SYN报文,在正常情况下,这个状态是服务器端的SOCKET在建立TCP连接时的三次握手会话过程中的一个中间状态,很短暂,基本上用netstat你是很难看到这种状态的,除非你特意写了一个客户端测试程序,故意将三次TCP握手过程中最后一个ACK报文不予发送。因此这种状态时,当收到客户端的ACK报文后,它会进入到ESTABLISHED状态。
  • -
  • SYN_SENT: 这个状态与SYN_RCVD遥想呼应,当客户端SOCKET执行CONNECT连接时,它首先发送SYN报文,因此也随即它会进入到了SYN_SENT状态,并等待服务端的发送三次握手中的第2个报文。SYN_SENT状态表示客户端已发送SYN报文。
  • -
  • ESTABLISHED:这个容易理解了,表示连接已经建立了。
  • -
  • FIN_WAIT_1: 这个状态要好好解释一下,其实FIN_WAIT_1和FIN_WAIT_2状态的真正含义都是表示等待对方的FIN报文。而这两种状态的区别是:FIN_WAIT_1状态实际上是当SOCKET在ESTABLISHED状态时,它想主动关闭连接,向对方发送了FIN报文,此时该SOCKET即进入到FIN_WAIT_1状态。而当对方回应ACK报文后,则进入到FIN_WAIT_2状态,当然在实际的正常情况下,无论对方何种情况下,都应该马上回应ACK报文,所以FIN_WAIT_1状态一般是比较难见到的,而FIN_WAIT_2状态还有时常常可以用netstat看到。
  • -
  • FIN_WAIT_2:上面已经详细解释了这种状态,实际上FIN_WAIT_2状态下的SOCKET,表示半连接,也即有一方要求close连接,但另外还告诉对方,我暂时还有点数据需要传送给你,稍后再关闭连接。
  • -
  • TIME_WAIT: 表示收到了对方的FIN报文,并发送出了ACK报文,就等2MSL后即可回到CLOSED可用状态了。如果FIN_WAIT_1状态下,收到了对方同时带FIN标志和ACK标志的报文时,可以直接进入到TIME_WAIT状态,而无须经过FIN_WAIT_2状态。
  • -
  • CLOSING: 这种状态比较特殊,实际情况中应该是很少见,属于一种比较罕见的例外状态。正常情况下,当你发送FIN报文后,按理来说是应该先收到(或同时收到)对方的ACK报文,再收到对方的FIN报文。但是CLOSING状态表示你发送FIN报文后,并没有收到对方的ACK报文,反而却也收到了对方的FIN报文。什么情况下会出现此种情况呢?其实细想一下,也不难得出结论:那就是如果双方几乎在同时close一个SOCKET的话,那么就出现了双方同时发送FIN报文的情况,也即会出现CLOSING状态,表示双方都正在关闭SOCKET连接。
  • -
  • CLOSE_WAIT: 这种状态的含义其实是表示在等待关闭。怎么理解呢?当对方close一个SOCKET后发送FIN报文给自己,你系统毫无疑问地会回应一个ACK报文给对方,此时则进入到CLOSE_WAIT状态。接下来呢,实际上你真正需要考虑的事情是察看你是否还有数据发送给对方,如果没有的话,那么你也就可以close这个SOCKET,发送FIN报文给对方,也即关闭连接。所以你在CLOSE_WAIT状态下,需要完成的事情是等待你去关闭连接。
  • -
  • LAST_ACK: 这个状态还是比较容易好理解的,它是被动关闭一方在发送FIN报文后,最后等待对方的ACK报文。当收到ACK报文后,也即可以进入到CLOSED可用状态了。
  • -
-

参考文献

TCP 的那些事儿(上) | | 酷 壳 - CoolShell
通俗大白话来理解TCP协议的三次握手和四次分手 · Issue #14 · jawil/blog · GitHub
面试常考题-TCP三次握手与四次握手 - CSDN博客
TCP协议中的三次握手和四次挥手(图解) - CSDN博客
Linux Socket 网络编程

- -
-
-
-
- -
- - - - - - -
- - - - - - -
- - - -
-
- - \ No newline at end of file diff --git "a/2020/01/23/linux fork\345\207\275\346\225\260\350\257\246\350\247\243/index.html" "b/2020/01/23/linux fork\345\207\275\346\225\260\350\257\246\350\247\243/index.html" deleted file mode 100644 index 2418201..0000000 --- "a/2020/01/23/linux fork\345\207\275\346\225\260\350\257\246\350\247\243/index.html" +++ /dev/null @@ -1,203 +0,0 @@ - - - - - - - -linux fork函数详解 - Genge 随笔 - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - -
-
-
-

linux fork函数详解

- -
-
-

函数原型: pid_t fork(void)

    -
  1. 参数:不需要参数
  2. -
  3. 需要的头文件 <sys/types.h><unistd.h>
  4. -
  5. 返回值分两种情况:
      -
    • 返回0表示成功创建子进程,并且接下来进入子进程执行流程
    • -
    • 返回PID(>0),成功创建子进程,并且继续执行父进程流程代码
    • -
    • 返回非正数(<0),创建子进程失败,失败原因主要有:
        -
      • 进程数超过系统所能创建的上限,errno会被设置为EAGAIN
      • -
      • 系统内存不足,errno会被设置为ENOMEM
      • -
      -
    • -
    -
  6. -
-

地址空间

-

使用 fork() 函数得到的子进程是父进程的一个复制品,它从父进程处继承了整个进程的地址空间:包括进程上下文(进程执行活动全过程的静态描述)、进程堆栈、打开的文件描述符、信号控制设定、进程优先级、进程组号等。子进程所独有的只有它的进程号,计时器等(只有小量信息)。因此,使用 fork() 函数的代价是很大的。

-
-

共享方式

-

实际上,更准确来说,Linux 的 fork() 使用是通过写时拷贝 (copy- on-write) 实现。写时拷贝是一种可以推迟甚至避免拷贝数据的技术。内核此时并不复制整个进程的地址空间,而是让父子进程共享同一个地址空间。只用在需要写入的时候才会复制地址空间,从而使各个进行拥有各自的地址空间。也就是说,资源的复制是在需要写入的时候才会进行,在此之前,只有以只读方式共享。

-
-

执行顺序

-

创建新进程成功后,系统中出现两个基本完全相同的进程,这两个进程执行没有固定的先后顺序,哪个进程先执行要看系统的进程调度策略。

-
-
-

linux有个类似的函数vfork():函数表面看起来都一样,但是它保证子进程先运行,在它调用 exec(进程替换) 或 exit(退出进程)之后父进程才可能被调度运行。子进程共享父进程的地址空间(准确来说,在调用 exec(进程替换) 或 exit(退出进程) 之前与父进程数据是共享的), vfork() 创建的子进程会执行完后,才到父进程执行。

-
-

区别

子进程与父进程的区别在于:

-
    -
  1. 除了文件锁以外,其他的锁都会被继承
  2. -
  3. 各自的进程ID和父进程ID不同
  4. -
  5. 子进程的未决告警被清除;
  6. -
  7. 子进程的未决信号集设置为空集。
  8. -
-

孤儿进程、僵尸进程

-

fork系统调用之后,父子进程将交替执行,执行顺序不定。如果父进程先退出,子进程还没退出那么子进程的父进程将变为init进程(托孤给了init进程)。(注:任何一个进程都必须有父进程)如果子进程先退出,父进程还没退出,那么子进程必须等到父进程捕获到了子进程的退出状态才真正结束,否则这个时候子进程就成为僵进程(僵尸进程:只保留一些退出信息供父进程查询)

-
-

多线程进程的Fork调用

坑大,面试可能会问道,工作中也要小心使用
云风 BLOG: 极不和谐的 fork 多线程程序
讲的主要是当前的进程processA (thread a/b/c)的当前子线程thread a调用fork后,会创建子进程,但是只是复制了thread a,总结一句就是所有父进程中别的线程,到了子进程中都是突然蒸发掉的。这样会导致各种死锁问题,以及各种数据不一致等问题。最好的办法是在多线程进程里不是用fork。如果非使用不可,尽量fork完毕后直接exec,不调用任何其他除了fork之外的函数。exec可以覆盖内存空间,可以解决所有关于锁的问题。
还有一些文章可以看看:

-
    -
  1. 谨防fork与锁之间的深坑 - CSDN博客
  2. -
  3. 子进程继承父进程中互斥锁的讨论 - CSDN博客
  4. -
  5. 在多线程中使用fork函数导致死锁,以及解决方案 - CSDN博客
  6. -
- -
-
-
-
- -
- - - - - - -
- - - - - - -
- - - -
-
- - \ No newline at end of file diff --git "a/2020/01/23/linux \345\256\211\350\243\205\346\214\207\345\256\232\347\211\210\346\234\254MySql/index.html" "b/2020/01/23/linux \345\256\211\350\243\205\346\214\207\345\256\232\347\211\210\346\234\254MySql/index.html" deleted file mode 100644 index e549525..0000000 --- "a/2020/01/23/linux \345\256\211\350\243\205\346\214\207\345\256\232\347\211\210\346\234\254MySql/index.html" +++ /dev/null @@ -1,211 +0,0 @@ - - - - - - - -linux 安装指定版本MySql - Genge 随笔 - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - -
-
-
-

linux 安装指定版本MySql

- -
-
-

由于工作环境、生产环境,我们使用的操作系统为为CentOS6.9,所需mysql版本为5.7,目前CentOS6.x系统默认mysql版本为5.1,这个版本是实在是太旧了。

-

彻底卸载系统已经安装的旧版本

    -
  • 检查系统已经安装的mysql
    rpm -qa|grep -i mysql

    -
  • -
  • 删除包
    rpm -ev mysql_lib_xxxx

    -
  • -
  • 删除老版本安装残留文件
    find / -iname mysql* 删除对应目录已经文件

    -
  • -
  • 删除my.cnf配置文件

    -
  • -
-

使用yum安装MySql5.7

    -
  • 下载mysql5.7源
    wget dev.mysql.com/get/mysql-community-release-el6-5.noarch.rpm

    -
  • -
  • 安装源
    yum localinstall mysql-community-release-el6-5.noarch.rpm

    -
  • -
  • 查看可用源中包含哪些版本并开启指定版本

    -
    yum repolist all | grep mysql
    -yum-config-manager --disable mysql56-community
    -yum-config-manager --disable mysql55-community
    -yum-config-manager --enable mysql57-community
    -
  • -
  • yum安装mysql
    yum install mysql-community-server

    -
  • -
  • 启动mysql
    service mysqld start

    -
  • -
  • 设置开机自动启动

    -
  • -
-
chkconfig --list | grep mysqld
-chkconfig mysqld on
- -
    -
  • 安装设置命令
    mysql_secure_installation
  • -
-

修改root密码

因为刚才启动的时候是系统默认配置的临时密码
使用如下命令可以查看,并且修改:

-
sudo grep 'temporary password' /var/log/mysqld.log
-mysql -u root -p 
-ALTER USER 'root'@'localhost' IDENTIFIED BY 'newPassword';
- -

设置允许连接数据库

命令如下:

-
mysql -u root -p 
-grant all privileges on *.* to root@"%" identified by 'passwordith grant option;  
-flush privileges;
- -

遇到的问题

    -
  • 比较奇怪,域名解析错误
    在yum install 的时候,发现大量的[Errno 14] PYCURL ERROR 6 - “Couldn’t resolve host ‘mirrors.aliyun.com’”错误,开始以为自己源设置错误,后来才知道,机器卡死过一次,导致系统莫名其妙的错误,了解网络的很快就知道需要设置系统的DNS
    /etc/resolv.conf文件中加入如下内容:
    nameserver 8.8.8.8
    - nameserver 114.114.114.114
    - 之前是啥也没有的,都是些没用的注释解释信息
  • -
- -
-
-
-
- -
- - - - - - -
- - - - - - -
- - - -
-
- - \ No newline at end of file diff --git "a/2020/01/23/linux \347\263\273\347\273\237\344\277\241\345\217\267\345\222\214\344\270\255\346\226\255\345\270\270\350\257\206/index.html" "b/2020/01/23/linux \347\263\273\347\273\237\344\277\241\345\217\267\345\222\214\344\270\255\346\226\255\345\270\270\350\257\206/index.html" deleted file mode 100644 index e4764c8..0000000 --- "a/2020/01/23/linux \347\263\273\347\273\237\344\277\241\345\217\267\345\222\214\344\270\255\346\226\255\345\270\270\350\257\206/index.html" +++ /dev/null @@ -1,264 +0,0 @@ - - - - - - - -linux 系统信号和中断常识 - Genge 随笔 - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - -
-
-
-

linux 系统信号和中断常识

- -
-
-

什么是中断

    -
  1. 中断基本概念

    -
    -

    中断是指计算机在执行期间,系统内发生任何非寻常的或非预期的急需处理事件,使得CPU暂时中断当前正在执行的程序而转去执行相应的事件处理程序,待处理完毕后又返回原来被中断处继续执行或调度新的进程执行的过程。引起中断发生的事件被称为中断源。中断源向CPU发出的请求中断处理信号称为中断请求,而CPU收到中断请求后转到相应的事件处理程序称为中断响应。
    在有些情况下,尽管产生了中断源和发出了中断请求,但CPU内部的处理器状态、字PSW的中断允许位已被清除,从而不允许CPU响应中断。这种情况称为禁止中断。CPU禁止中断后只有等到PSW的中断允许位被重新设置后才能接收中断。禁止中断也称为关中断,PSW的中断允许位的设置也被称为开中断。开中断和关中断是为了保证某段程序执行的原子性。
    还有一个比较常用的概念是中断屏蔽。中断屏蔽是指在中断请求产生之后,系统有选择地封锁一部分中断而允许另一部分中断仍能得到响应。不过,有些中断请求是不能屏蔽甚至不能禁止的,也就是说,这些中断具有最高优先级,只要这些中断请求一旦提出,CPU必须立即响应。例如,电源掉电事件所引起的中断就是不可禁止和不可屏蔽的。

    -
    -
  2. -
  3. 中断分类与等级

    -
    -

    根据系统对中断处理的需要,操作系统一般对中断进行分类并对不同的中断赋予不同的处理优先级,以便在不同的中断同时发生时,按轻重缓急进行处理。
    根据中断源产生的条件,可把中断分为外中断和内中断。外中断是指来自处理器和内存外部的中断,包括I/0设备发出的I/O中断、外部信号中断(例如用户键人ESC键)。各种定时器引起的时钟中断以及调试程序中设置的断点等引起的调试中断等。外中断在狭义上一般被称为中断。
    内中断主要指在处理器和内存内部产生的中断。内中断一般称为陷阱(trap)或异常。它包括程序运算引起的各种错误,如地址非法、校验错、页面失效、存取访问控制错、算术操作溢出、数据格式非法、除数为零、非法指令、用户程序执行特权指令、分时系统中的时间片中断以及从用户态到核心态的切换等都是陷阱的例子。
    为了按中断源的轻重缓急处理响应中断,操作系统为不同的中断赋予不同的优先级。例如在UNIX系统中,外中断和陷阱的优先级共分为8级。为了禁止中断或屏蔽中断,CPU的处理器状态字PSW中也设有相应的优先级。如果中断源的优先级高于PSW的优先级,则CPU响应该中断源的请求;反之,CPU屏蔽该中断源的中断请求。
    各中断源的优先级在系统设计时给定,在系统运行时是固定的。而处理器的优先级则根据执行情况由系统程序动态设定。
    除了在优先级的设置方面有区别之外,中断和陷阱还有如下主要区别:
    陷阱通常由处理器正在执行的现行指令引起,而中断则是由与现行指令无关的中断源引起的。陷阱处理程序提供的服务为当前进程所用,而中断处理程序提供的服务则不是为了当前进程的。
    CPU执行完一条指令之后,下一条指令开始之前响应中断,而在一条指令执行中也可以响应陷阱。例如执行指令非法时,尽管被执行的非法指令不能执行结束,但CPU仍可对其进行处理。

    -
    -
  4. -
  5. 软中断

    -
    -

    软中断的概念主要来源于UNIX系统。软中断是对应于硬中断而言的。通过硬件产生相应的中断请求,称为硬中断。而软中断则不然,它是在通信进程之间通过模拟硬中断而实现的一种通信方式。中断源发出软中断信号后,CPU或者接收进程在“适当的时机”进行中断处理或者完成软中断信号所对应的功能。这里“适当的时机”,表示接收软中断信号的进程须等到该接收进程得到处理器之后才能进行。如果该接收进程是占据处理器的,那么,该接收进程在接收到软中断信号后将立即转去执行该软中断信号所对应的功能。

    -
    -
  6. -
  7. 中断处理过程
    一旦CPU响应中断,转人中断处理程序,系统就开始进行中断处理。下面对中断处理过程进行详细说明:

    -
      -
    1. CPU检查响应中断的条件是否满足。CPU响应中断的条件是:有来自于中断源的中断请求、CPU允许中断。如果中断响应条件不满足,则中断处理无法进行。

      -
    2. -
    3. 如果CPU响应中断,则CPU关中断,使其进入不可再次响应中断的状态。

      -
    4. -
    5. 保存被中断进程现场。为了在中断处理结束后能使进程正确地返回到中断点,系统必须保存当前处理器状态字PSW和程序计数器PC等的值。这些值一般保存在特定堆栈或硬件寄存器中。

      -
    6. -
    7. 分析中断原因,调用中断处理子程序。在多个中断请求同时发生时,处理优先级最高的中断源发出的中断请求。在系统中,为了处理上的方便,通常都是针对不同的中断源编制有不同的中断处理子程序(陷阱处理子程序)。这些子程序的人口地址(或陷阱指令的人口地址)存放在内存的特定单元中。再者,不同的中断源也对应着不同的处理器状态字PSW。这些不同的PSW被放在相应的内存单元中,与中断处理子程序人口地址一起构成中断向量。显然,根据中断或陷阱的种类,系统可由中断向量表迅速地找到该中断响应的优先级、中断处理子程序(或陷阱指令)的入口地址和对应的PSW。

      -
    8. -
    9. 执行中断处理子程序。对陷阱来说,在有些系统中则是通过陷阱指令向当前执行进程发出软中断信号后调用对应的处理子程序执行。

      -
    10. -
    11. 退出中断,恢复被中断进程的现场或调度新进程占据处理器。

      -
    12. -
    13. 开中断,CPU继续执行。

      -
    14. -
    -
  8. -
  9. 设备管理程序与中断方式

    -
    -

    处理器的高速和输入输出设备低速之间的矛盾,是设备管理要解决的一个重要问题。为了提高整体效率,减少在程序直接控制方式中的CPU等待时间以及提高系统的并行工作效率,采用中断方式来控制输入输出设备和内存与CPU之间的数据传送,是很有必要的。在硬件结构上,这种方式要求CPU与输入输出设备(或控制器)之间有相应的中断请求线,而且在输入输出设备控制器的控制状态寄存器上有相应的中断允许位。

    -
    -
  10. -
-

信号

信号驱动的异步I/O是指一旦设备准备好,就主动通知应用程序,这种情况下应用程序就不需要查询设备状态。异步 I/O 和硬件上常提的中断的概念类似,信号是在软件层次上对中断机制的一种模拟。

-
    -
  1. 信号通信机制
    软中断信号(signal,又简称为信号)是进程间通信机制中唯一的异步通信机制,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号到底什么时候到达。
    进程之间可以互相通过系统调用kill发送软中断信号,内核也可以因为内部事件而给进程发送信号,通知进程发生了某个事件。信号机制除了基本通知功能外,还可以传递附加信息。

    -
  2. -
  3. 处理信号
    收到信号的进程对各种信号有不同的处理方法,主要分为以下三类:

    -
      -
    • 类似中断的处理程序,对于需要处理的信号,进程可以指定处理函数,由该函数来处理。进程通过系统调用signal来指定进程对某个信号的处理行为。
    • -
    • 忽略某个信号,对该信号不做任何处理,就象未发生过一样。
    • -
    • 对该信号的处理保留系统的默认值,对大部分的信号的缺省操作是使得进程终止。
    • -
    -
  4. -
-

需要注意的是,信号处理函数注册后,当信号来临并被触发调用信号处理函数,当同样类型信号再次到来时,并不会执行信号处理函数,而是使用信号的系统默认处理方式,大部分都是使得进程终止。

-
    -
  1. 信号可靠性
    从可靠性方面信号分为可靠信号与不可靠信号;信号的可靠与不可靠只与信号值有关,与信号的发送及安装函数无关。信号值位于 SIGRTMIN 和 SIGRTMAX 之间的信号都是可靠信号,可靠信号克服了信号可能丢失的问题。
    非实时信号都不支持排队,都是不可靠信号;实时信号都支持排队,都是可靠信号。
    不可靠信号就是指发送的信号内核不一定能够发送给 目标进程,信号可能丢失。 不可靠信号在内核中存储的方式是位图和链表,当内核接收一个信号后,先判断它是否 已经存在,不存在就把他对应的位置一,并将信号挂入链表,存在则丢弃信号。 可靠信号则是把信号放入队列,再链入链表,所以也就保证了信号不丢失。
    非可靠信号一般都有确定的用途及含义, 可靠信号则可以让用户自定义使用
    需要注意的是:这里的“实时”和实时操作系统中的“实时”没有任何联系,实时信号在处理速度上并不会比普通信号快,它们之间的区别就是:普通信号会对多次的同一个信号进行“合并”处理,而实时信号会一一处理。这就要求我们在编写信号监听函数时,要捕获普通信号,必须时刻轮训监听,因为系统默认会丢弃同种类型的普通信号!

    -
  2. -
  3. 信号传递顺序
    如果存在多个未决信号,同一个未决信号会按照发送顺序来递送信号,不同的未决信 号按照信号的序号大小来递送,序号小的信号会先被递送到进程。另外,linux中会优 先递送不可靠信号
    Linux中的信号机制优先级是:高优先级中断->低优先级中断->软中断->信号->进程运行。
    需要注意的是,在用户态不存在未决信号。信号处理一般发生在进程从内核态返回用户态的时候。内核空间没有信号处理机制,内核态也不会处理信号否者信号拥有系统最高权限,变得不再安全

    -
  4. -
  5. 多线程中信号造成死锁
    如果一个线程持有锁,在操作临界区内容时,被信号中断了,转而去执行信号处理函数, 而信号处理函数再次对临界区加锁就会造成死锁。
    解决的方法就是使用信号等待函数,线程阻塞等待信号处理函数直到处理完毕,也就是 所说的化异步为同步。

    -
  6. -
  7. 信号响应过程

    -
      -
    1. A进程调用信号发送函数,发送信号给B,这是软中断,所以A进程会进入内核态运行操作系统的信号调度代码
    2. -
    3. 操作系统发现B进程正在运行,于是写入管理B进程的某个数据结构
    4. -
    5. 操作系统返回给A,A继续执行
    6. -
    7. B进程分配的处理器时间用完了,被时钟硬件中断
    8. -
    9. 操作系统的时钟硬件中断处理函数准备挂起B进程,也就是把寄存器和函数堆栈保存起来,发现B进程收到了singal
    10. -
    11. 操作系统在保存好B进程的stack和register后,新开stack(为了不干扰B进程真正的代码stack),激活B进程,B进程的信号处理函数。
    12. -
    -
  8. -
  9. 信号生命周期(和响应过程类似)

    -
      -
    • 在目的进程中安装该信号。即设置捕获该信号时进程执行的操作,采用signal 或者 sigaction 系统调用来实现。
    • -
    • 信号被某个进程产生,同时设置该信号的目的进程(使用pid),之后交给操作系统进行管理。采用kill()、arise()、alarm()等系统调用来实现。
    • -
    • 信号在目的进程被注册。就是把信号值加入到进程的PCB(task_struct)中相关的数据结构里——未决信号的数据成员,信号携带的其他信息被保留到未决信的队列的某个sigqueue结构中。
    • -
    • 信号在进程中注销。在执行信号处理函数前,要把信号在进程中注销。
    • -
    • 信号生命的终结。进程终止当前的工作,保护上下文,执行信号处理函数,之后恢复。
    • -
    -
  10. -
  11. 信号阻塞集(屏蔽集、掩码)
    信号阻塞集也称信号屏蔽集、信号掩码。每个进程都有一个阻塞集,创建子进程时子进程将继承父进程的阻塞集。信号阻塞集用来描述哪些信号递送到该进程的时候被阻塞(在信号发生时记住它,直到进程准备好时再将信号通知进程)。
    所谓阻塞并不是禁止传送信号, 而是暂缓信号的传送。若将被阻塞的信号从信号阻塞集中删除,且对应的信号在被阻塞时发生了,进程将会收到相应的信号。

    -
  12. -
-

信号实现机制

    -
  1. 发送信号
    内核给一个进程发送软中断信号的方法,是在进程所在的进程表项的信号域设置对应于该信号的位。如果信号发送给一个正在睡眠的进程,那么要看该进程进入睡眠的优先级,如果进程睡眠在可被中断的优先级上,则唤醒进程;否则仅设置进程表中信号域相应的位,而不唤醒进程。
    进程的 PCB 中有关于本进程中未决信号的数据成员 struct sigpending pending
    struct sigpending{
    -    struct sigqueue *head, *tail;
    -    sigset_t signal;
    -};
    -
  2. -
-

第三个成员是进程中所有未决信号集,第一、第二个成员分别指向一个sigqueue类型的结构链(称之为”未决信号信息链”)的首尾,信息链中的每个sigqueue结构刻画一个特定信号所携带的信息,并指向下一个sigqueue结构:

-
struct sigqueue{
-    struct sigqueue *next;
-    siginfo_t info;
-}
- -

信号在进程中注册指的就是信号值加入到进程的未决信号集sigset_t signal(每个信号占用一位)中,并且信号所携带的信息被保留到未决信号信息链的某个sigqueue结构中。只要信号在进程的未决信号集中,表明进程已经知道这些信号的存在,但还没来得及处理,或者该进程被信号阻塞。

-

当一个可靠信号发送给一个进程时,不管该信号是否已经在进程中注册,都会被再注册一次,因此,信号不会丢失。这意味着同一个可靠信号可以在同一个进程的未决信号信息链中占有多个sigqueue结构(进程每收到一个可靠信号,都会为它分配一个结构来注册该信号信息,并把该结构添加在未决信号链尾)。

-

当一个非可靠信号发送给一个进程时,如果该信号已经在进程中注册(通过sigset_t signal指示),则该信号将被丢弃,造成信号丢失。这意味着同一个非实时信号在进程的未决信号信息链中,至多占有一个sigqueue结构。

-

总之信号注册与否,与发送信号的函数(如kill()或sigqueue()等)以及信号安装函数(signal()及sigaction())无关,只与信号值有关(信号值小于SIGRTMIN的信号最多只注册一次,信号值在SIGRTMIN及SIGRTMAX之间的信号,只要被进程接收到就被注册)。

-
    -
  1. 处理信号
    内核处理一个进程收到的信号的时机是在一个进程从内核态返回用户态时。所以,当一个进程在内核态下运行时,软中断信号并不立即起作用,要等到将返回用户态时才处理。进程只有处理完信号才会返回用户态,进程在用户态下不会有未处理完的信号。
    内核处理一个进程收到的软中断信号是在该进程的上下文中,因此,进程必须处于运行状态。当进程接收到一个它忽略的信号时,进程丢弃该信号,就像没有收到该信号似的继续运行。
    如果进程收到一个要捕捉的信号,那么进程从内核态返回用户态时执行用户定义的函数。而且执行用户定义的函数的方法很巧妙,内核在用户栈上创建一个新的层,该层中将返回地址的值设置成用户定义的处理函数的地址,这样进程从内核返回弹出栈顶时就返回到用户定义的函数处,从函数返回再弹出栈顶时,才返回原先进入内核的地方。这样做的原因是用户定义的处理函数不能且不允许在内核态下执行(如果用户定义的函数在内核态下运行的话,用户就可以获得任何权限)。
    对于非可靠信号来说,由于在未决信号信息链中最多只占用一个sigqueue结构,因此该结构被释放后,应该把信号在进程未决信号集中删除(信号注销完毕);而对于实时信号来说,可能在未决信号信息链中占用多个sigqueue结构,因此应该针对占用sigqueue结构的数目区别对待:如果只占用一个sigqueue结构(进程只收到该信号一次),则执行完相应的处理函数后应该把信号在进程的未决信号集中删除(信号注销完毕)。否则待该信号的所有sigqueue处理完毕后再在进程的未决信号集中删除该信号。
    当所有未被屏蔽的信号都处理完毕后,即可返回用户空间。对于被屏蔽的信号,当取消屏蔽后,在返回到用户空间时会再次执行上述检查处理的一套流程。
  2. -
-

参考文献

CS_Offer/Signal.md at master · xuelangZF/CS_Offer · GitHub
信号处理的时机
面试中关于Linux的信号常问的问题 | 等英博客
linux系统编程之信号(一):中断与信号 - mickole - 博客园
Linux系统编程——进程间通信:信号中断处理 - CSDN博客

- -
-
-
-
- -
- - - - - - -
- - - - - - -
- - - -
-
- - \ No newline at end of file diff --git "a/2020/01/23/\345\205\250\345\261\200\345\224\257\344\270\200ID\347\224\237\346\210\220\347\256\227\346\263\225\344\274\230\345\214\226/index.html" "b/2020/01/23/\345\205\250\345\261\200\345\224\257\344\270\200ID\347\224\237\346\210\220\347\256\227\346\263\225\344\274\230\345\214\226/index.html" deleted file mode 100644 index cdeb3cd..0000000 --- "a/2020/01/23/\345\205\250\345\261\200\345\224\257\344\270\200ID\347\224\237\346\210\220\347\256\227\346\263\225\344\274\230\345\214\226/index.html" +++ /dev/null @@ -1,710 +0,0 @@ - - - - - - - -全局唯一ID生成算法优化 - Genge 随笔 - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - -
-
-
-

全局唯一ID生成算法优化

- -
-
- - - - - -

前言

在进程启动前,一般会给每个进程静态分配一个唯一标识ID(ServerID或者PipeID)

-
struct SServerID
-{
-    UINT16 wPlatform = 0;	 ///< Platform   平台id 
-    UINT16 wArea = 0;        ///< Area       区服id
-    UINT16 wType = 0;        ///< Type       服务器App类型
-    UINT16 wIndex = 0;       ///< Index      服务器App编号索引
-};
- -

每个Role/Item/Hero/Mail 等创建的时候都会创建一个UUID来唯一标识,其中Mail_Uuid还有趋势递增的需求

-

Snowflake算法介绍

SnowFlake分布式生成Id算法由Twitter开源

-

SnowFlake算法生成id的结果是一个64bit大小的UINT64整数,它的结构如下图:
snowflake uuid-64bit

-

SnowFlake的优点:
- 整体上按照时间自增排序
- 整个分布式系统内不会产生ID碰撞(时间戳和自增序列以外的字段作区分), 并且效率较高(位运算),

-

SnowFlake每秒能够产生6.4万ID左右.(和5段位的配置位数有关)
UUID从高位到低位依次排列:

-
    -
  • 第一段:39位, 当对于某一个时间点的时间戳差值(至少10年可用)
  • -
  • 第二段:3 位, 平台platform或大区id,比如QQ Android/QQ IOS/Wechat IOS 等(8)
  • -
  • 第三段:11位, 区服area id,对应的就是服务器小区的ID(2048)
  • -
  • 第四段:5 位, 服务器APP实例id index(32), Notice:appid需要在area范围内唯一
  • -
  • 第五段:6 位, 自增长id,也就是说在一个完整的毫秒时间内最多可以生成64个UUID
  • -
-

SnowFlake算法优化

目前算法设计缺陷

在实际服务器运行过程中,尤其在游戏服务器开发期间,大量用户注册,会有瞬间生成大量UUID的需求

-

之前服务器的做法是:

-
//遍历了一圈64个,等下一个毫秒生成
-if (m_nGlobalSeq == m_nMliSeq)
-{
-    // 毫秒内序列溢出, 阻塞到下一个毫秒,获得新的时间戳
-    nCurTimestamp = WaitForNextMilli(m_nLastTimestamp);
-}
-
-/**
-* 阻塞到下一个毫秒,直到获得新的时间戳
-*
-* @param lastTimestamp 上次生成ID的时间截
-* @return 当前时间戳
-*/
-UINT64 WaitForNextMilli(UINT64 lastTimestamp) const
-{
-    UINT64 nTimestamp = _GetNowMliTime();
-    while (nTimestamp <= lastTimestamp)
-    {
-        nTimestamp = _GetNowMliTime();
-    }
-    return nTimestamp;
-}
-
-

这里使用while强行等待到下一毫秒,相当于阻塞当前线程,从而成为热点函数,需要去优化

-

设计优化

    -
  • 合理分配各字段占用bit(图中已经是调整过的)
  • -
  • 将生成uuid单独出一个独立全局Server组件,提供全局唯一UUID服务,这样除去高位39bit外理论上其他bit都可以当作自增位,而且可以提前生成很多uuid放在pool当中,有需要的进程从当中取,可以满足需求,Baidu在github上开源的UUID生成算法就是这样处理的
  • -
  • 我们取一个折中方案,只是在server中加一个Pool,存放提前生成好的UUIDs,每次业务端需要UUID的时候首先从Pool中取,如果取不到就走原来的流程
  • -
-
UINT64 GenId()
-{
-    if (!IsEmpty())
-    {
-        //从Pool中取
-        return PopFrontElement();
-    }
-
-    return NextId();
-}
- -

既然存在Pool,那么就需要设计Pool中元素填充方案
首先将Pool设定一个合适的固定最大值 const UINT32 UUID_POOL_MAX_SIZE = 1 << 13; // uuid pool 最大数 8192
然后根据当前Pool的状态,来定时填充,每次填充的数量为当前毫秒内所有可以生成的UUID数
Fill_Pool.png

-
#include "uuid_pool_mgr.h"
-#include "uuid_generator.h"
-
-
-bool CUUIDPool::Init()
-{
-    FillUuidPool();
-    return true;
-}
-
-void CUUIDPool::OnTimer(unsigned int nId)
-{
-    EXLOG_DEBUG << "[RyzUuid]CUUIDPool::OnTimer Old EUuidPoolState : " << nId;
-    CUUIDMaker::Instance()->FillPoolWithInMli();
-    FillUuidPool();
-}
-
-void CUUIDPool::FillUuidPool()
-{
-    EUuidPoolState eUuidDequeState = CUUIDMaker::Instance()->GetUuidDequeState();
-    uint32 nUpdateInterval = 5 * 60 * 1000;
-    switch (eUuidDequeState)
-    {
-    case EUuidPoolState_Empty:
-        nUpdateInterval = 1 * 60 * 1000;
-        break;
-    case EUuidPoolState_Not_Full_0_30_Per:
-        nUpdateInterval = 2 * 60 * 1000;
-        break;
-    case EUuidPoolState_Not_Full_30_70_Per:
-        nUpdateInterval = 3 * 60 * 1000;
-        break;
-    case EUuidPoolState_Not_Full_70_100_Per:
-        nUpdateInterval = 4 * 60 * 1000;
-        break;
-    case EUuidPoolState_Full:
-        nUpdateInterval = 5 * 60 * 1000;
-        break;
-    default:
-        nUpdateInterval = 5 * 60 * 1000;
-        break;
-    }
-
-    EXLOG_DEBUG << "[RyzUuid]CUUIDPool::OnTimer Now EUuidPoolState : " << eUuidDequeState;
-    SetTimer(eUuidDequeState, nUpdateInterval, nUpdateInterval, ETIMER_ONCE);
-}
-
-
-enum EUuidPoolState
-{
-    EUuidPoolState_Empty				= 1,
-    EUuidPoolState_Not_Full_0_30_Per	= 2,
-    EUuidPoolState_Not_Full_30_70_Per	= 3,
-    EUuidPoolState_Not_Full_70_100_Per	= 4,
-    EUuidPoolState_Full					= 5,
-};
-
-//uuid_generator.h
-//将当前毫秒内的UUID全部生成并存到Pool中
-void FillPoolWithInMli()
-{
-    EUuidPoolState nCurState = GetUuidDequeState();
-    AtomicUInt64 nCurTimestamp = _GetNowMliTime();
-    while (nCurState != EUuidPoolState_Full)
-    {
-        UINT64 nNextUUId = NextId(false);
-        if (nNextUUId == 0)
-        {
-            break;
-        }
-
-        PushBackElement(nNextUUId);
-
-        if (nCurTimestamp != _GetNowMliTime())
-        {
-            break;
-        }
-    }
-}
-
- -

是否这样就完美了呢?

并没有呢!!!
这个算法强制依赖时间递增,如果时间回拨怎么办?
目前的做法是直接throw new exception
分析时间回拨产生原因
第一:人物操作,在真实环境一般不会有那个傻逼干这种事情,所以基本可以排除。
第二:由于有些业务等需要,机器需要同步时间服务器(在这个过程中可能会存在时间回拨,查了下我们服务器一般在10ms以内(2小时同步一次))。 Ntp过程可能产生时间回拨。
第三:QA和策划测试过程中有需求怎么办?
解决办法:

-
    -
  1. 将uuid_generation独立出来给其他server提供服务
  2. -
  3. 当回拨时间小于XXms,就等时间追上来之后继续生成。 (XXms对业务没有什么影响)
  4. -
  5. 当时间大于XXms时间我们通过更换AppId位来来解决回拨问题。
  6. -
-

Talk is cheap, show you the code

#ifndef __UUID_GENERATOR_H__
-#define __UUID_GENERATOR_H__
-
-#include "gnsingleton.h"
-#include "gntype.h"
-#include "gnpipe.h"
-#include "gntime.h"
-#include "noncopy.h"
-#include "gnserverid.h"
-
-#include <assert.h>
-#include <mutex>
-#include <atomic>
-#include <chrono>
-#include <exception>
-#include <sstream>
-#include <deque>
-
-enum EUuidPoolState
-{
-    EUuidPoolState_Empty				= 1,
-    EUuidPoolState_Not_Full_0_30_Per	= 2,
-    EUuidPoolState_Not_Full_30_70_Per	= 3,
-    EUuidPoolState_Not_Full_70_100_Per	= 4,
-    EUuidPoolState_Full					= 5,
-};
-
-//#define SNOWFLAKE_ID_MAKER_NO_LOCK
-class CSnowflakeIdMaker : private CNoncopy
-{
-public:
-#ifdef SNOWFLAKE_ID_MAKER_NO_LOCK
-    typedef std::atomic<UINT32> AtomicUInt;
-    typedef std::atomic<UINT64> AtomicUInt64;
-#else
-    typedef UINT32 AtomicUInt;
-    typedef UINT64 AtomicUInt64;
-#endif
-
-    const UINT32 UUID_POOL_MAX_SIZE = 1 << 13;   //		uuid pool 最大数 8192
-    const UINT64 START_EPOCH			= 1541001600000LL;		//开始时间截 (2018-11-01 00:00:00.000),修改此时间可调整可用时长
-                               
-    const UINT32 A_TIMESTAMP_BITS		= 39;					//时间戳所占的位数
-    const UINT32 B_PLATFORM_BITS		= 3;					//平台id所占的位数
-    const UINT32 C_AREA_BITS			= 11;					//区服id所占的位
-    const UINT32 D_APP_ID_BITS			= 5;					//app id所占的位数
-    const UINT32 E_INCR_SEQUENCE_BITS	= 6;				    //自增序列所占的位数
-
-    const UINT32 APP_ID_SHIFT			= E_INCR_SEQUENCE_BITS;		//APPID向左移位数
-    const UINT32 AREA_ID_SHIFT			= E_INCR_SEQUENCE_BITS + D_APP_ID_BITS;											//小区id向左移位数
-    const UINT32 PLATFORM_ID_SHIFT		= E_INCR_SEQUENCE_BITS + D_APP_ID_BITS + C_AREA_BITS;						//大区id向左移位数
-    const UINT32 TIME_STAMP_SHIFT		= E_INCR_SEQUENCE_BITS + D_APP_ID_BITS + C_AREA_BITS + B_PLATFORM_BITS;		//时间戳向左移位数
-    const UINT32 SEQUENCE_MASK			= (1 << E_INCR_SEQUENCE_BITS) - 1;												//生成序列的掩码
-
-
-    CSnowflakeIdMaker() : m_nPlatformId(0), m_nAreaId(0), m_nGlobalSeq(0), m_nLastTimestamp(0) {}
-
-    CSnowflakeIdMaker(const UINT32 nPlatId, const UINT32 nAreaId, const UINT32 nAppId)
-    {
-        Init(nPlatId, nAreaId, nAppId);
-    }
-
-    void Init(const UINT32 nPlatId, const UINT32 nAreaId, const UINT32 nAppId)
-    {
-        m_nPlatformId = nPlatId;
-        m_nAreaId = nAreaId;
-        m_nAppId = nAppId;
-    }
-
-    UINT64 GenId()
-    {
-        if (!IsEmpty())
-        {
-            return PopFrontElement();
-        }
-
-        return NextId();
-    }
-
-    /**
-    * 获得下一个ID (该方法是线程安全的)
-    * @param  bCanBlock 参数指定当前函数是否可以阻塞, 默认为true
-    * @return SnowflakeId
-    */
-    UINT64 NextId(bool bCanBlock = true)
-    {
-        using namespace  std;
-
-#ifdef SNOWFLAKE_ID_MAKER_NO_LOCK
-        static AtomicUInt64 nCurTimestamp{ 0 };
-#else
-        std::unique_lock<std::mutex> oLock{ m_oMutex };
-        AtomicUInt64 nCurTimestamp{ 0 };
-#endif
-
-        nCurTimestamp = _GetNowMliTime();
-
-        // 如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
-        if (nCurTimestamp < m_nLastTimestamp)
-        {
-            std::ostringstream oSS;
-            oSS << "clock moved backwards.  Refusing to generate id for " << m_nLastTimestamp - nCurTimestamp << " milliseconds";
-            throw std::exception(std::runtime_error(oSS.str()));
-        }
-
-        m_nGlobalSeq = (m_nGlobalSeq + 1) & SEQUENCE_MASK;
-
-        // 为了使id递增+1均匀分布,这里seq跨毫秒也不清0
-        if (m_nLastTimestamp == nCurTimestamp)
-        {
-            //遍历了一圈64个,等下一个毫秒生成
-            if (m_nGlobalSeq == m_nMliSeq)
-            {
-                if (bCanBlock)
-                {
-                    // 毫秒内序列溢出, 阻塞到下一个毫秒,获得新的时间戳
-                    nCurTimestamp = WaitForNextMilli(m_nLastTimestamp);
-                }
-                else
-                {
-                    return 0;
-                }
-            }
-        }
-        else
-        {	
-            m_nMliSeq = m_nGlobalSeq;
-        }
-
-#ifdef SNOWFLAKE_ID_MAKER_NO_LOCK
-        m_nLastTimestamp = nCurTimestamp.load();
-#else
-        m_nLastTimestamp = nCurTimestamp;
-#endif
-
-        // 移位并通过或运算拼到一起组成64位的ID
-        return ((nCurTimestamp - START_EPOCH) << TIME_STAMP_SHIFT)
-            | (m_nPlatformId << PLATFORM_ID_SHIFT)
-            | (m_nAreaId << AREA_ID_SHIFT)
-            | (m_nAppId << APP_ID_SHIFT)
-            | (m_nGlobalSeq);
-    }
-
-    EUuidPoolState GetUuidDequeState() 
-    {
-        if (m_deUuidPool.empty())
-        {
-            return EUuidPoolState_Empty;
-        }
-
-        size_t nCurPoolSize = m_deUuidPool.size();
-        if (nCurPoolSize >= UUID_POOL_MAX_SIZE)
-        {
-            return EUuidPoolState_Full;
-        }
-
-        uint32 nCurPercent = nCurPoolSize * 100 / UUID_POOL_MAX_SIZE;
-        if (nCurPercent < 30)
-        {
-            return EUuidPoolState_Not_Full_0_30_Per;
-        }
-        else if (nCurPercent < 70)
-        {
-            return EUuidPoolState_Not_Full_30_70_Per;
-        }
-        else
-        {
-            return EUuidPoolState_Not_Full_70_100_Per;
-        }
-
-        return EUuidPoolState_Full;
-    } 
-
-    void FillPoolWithInMli()
-    {
-        EUuidPoolState nCurState = GetUuidDequeState();
-        AtomicUInt64 nCurTimestamp = _GetNowMliTime();
-        while (nCurState != EUuidPoolState_Full)
-        {
-            UINT64 nNextUUId = NextId(false);
-            if (nNextUUId == 0)
-            {
-                break;
-            }
-            
-            PushBackElement(nNextUUId);
-
-            if (nCurTimestamp != _GetNowMliTime())
-            {
-                break;
-            }
-        }
-    }
-
-protected:
-
-    //判断pool是否为空
-    bool IsEmpty()
-    {
-        return m_deUuidPool.empty();
-    }
-
-    //返回pool队列中front 元素
-    UINT64 PopFrontElement()
-    {
-#ifdef SNOWFLAKE_ID_MAKER_NO_LOCK
-        
-#else
-        std::unique_lock<std::mutex> oLock{ m_oMutex };
-#endif
-
-        UINT64 nFrontElement = m_deUuidPool.front();
-        m_deUuidPool.pop_front();
-        return nFrontElement;
-    }
-
-    void PushBackElement(UINT64 nNextUUId)
-    {
-#ifdef SNOWFLAKE_ID_MAKER_NO_LOCK
-#else
-        std::unique_lock<std::mutex> oLock{ m_oMutex };
-#endif
-        m_deUuidPool.push_back(nNextUUId);
-    }
-
-    /**
-    * 返回以毫秒为单位的当前时间
-    *
-    * @return 当前时间(毫秒)
-    */
-    UINT64 _GetNowMliTime() const
-    {
-        if (0)
-        {
-            Storm::CSTDateTime oDateTime;
-            oDateTime.Now();
-            return oDateTime.EpochMilliSecs();
-        }
-        else
-        {
-            using namespace std;
-            auto nTimeNow = chrono::system_clock::now();
-            auto nDurationInMs = chrono::duration_cast<chrono::milliseconds>(nTimeNow.time_since_epoch());
-            return nDurationInMs.count();
-        }
-    }
-
-    /**
-    * 阻塞到下一个毫秒,直到获得新的时间戳
-    *
-    * @param lastTimestamp 上次生成ID的时间截
-    * @return 当前时间戳
-    */
-    UINT64 WaitForNextMilli(UINT64 lastTimestamp) const
-    {
-        UINT64 nTimestamp = _GetNowMliTime();
-        while (nTimestamp <= lastTimestamp)
-        {
-            nTimestamp = _GetNowMliTime();
-        }
-        return nTimestamp;
-    }
-
-private:
-
-#ifndef SNOWFLAKE_ID_MAKER_NO_LOCK
-    std::mutex		m_oMutex;
-#endif
-
-    UINT32			m_nPlatformId = 0;		//平台id
-    UINT32			m_nAreaId = 0;			//区服id
-    UINT32			m_nAppId = 0;			//Appid
-    AtomicUInt		m_nGlobalSeq{ 0 };		//全局序列
-    AtomicUInt		m_nMliSeq{ 0 };			//每毫秒序列
-    AtomicUInt64	m_nLastTimestamp{ 0 };	//上次生成ID的时间截
-    std::deque<UINT64> m_deUuidPool;			//uuid池 用于存放预生成uuids
-};
-
-
-
-/************************************************************************/
-/* 负责生成全局唯一id                                                */
-/************************************************************************/
-class CUUIDMaker : public Storm::TSingleton<CUUIDMaker>
-{
-    friend class Storm::TSingleton<CUUIDMaker>;
-public:
-    bool Init(const UINT32 nParaA, const UINT32 nParaB, const UINT32 nParaC)
-    {
-        m_oIdMaker.Init(nParaA, nParaB, nParaC);
-        return true;
-    }
-    
-    /// init with pipeid
-    bool Init(const UINT64 nPipeId)
-    {
-        using namespace Storm;
-        CServerID oServID(nPipeId);
-        m_oIdMaker.Init(oServID.GetPlat(), oServID.GetArea(), oServID.GetIndex());
-        return true;
-    }
-
-    UINT64 GenId()
-    {
-        return m_oIdMaker.GenId();
-    }
-
-    EUuidPoolState GetUuidDequeState()
-    {
-        return m_oIdMaker.GetUuidDequeState();
-    }
-
-    void FillPoolWithInMli()
-    {
-        m_oIdMaker.FillPoolWithInMli();
-    }
-
-    UINT64 GetCompareIdFromTime(UINT32 nTimeVal);
-
-    UINT64 GetTimeAddVal(UINT32 nTimeVal);
-
-    UINT32 GetTimeValFromUuid(UINT64 nUuid);
-
-private:
-    CSnowflakeIdMaker	m_oIdMaker;
-};
-
-#define  GEN_GLOBAL_UUID()  CUUIDMaker::Instance()->GenId()
-
-
-#endif
-
-
-//gameserver 
-#include "uuid_pool_mgr.h"
-#include "uuid_generator.h"
-
-
-bool CUUIDPool::Init()
-{
-    FillUuidPool();
-    return true;
-}
-
-void CUUIDPool::OnTimer(unsigned int nId)
-{
-    EXLOG_DEBUG << "[RyzUuid]CUUIDPool::OnTimer Old EUuidPoolState : " << nId;
-    CUUIDMaker::Instance()->FillPoolWithInMli();
-    FillUuidPool();
-}
-
-void CUUIDPool::FillUuidPool()
-{
-    EUuidPoolState eUuidDequeState = CUUIDMaker::Instance()->GetUuidDequeState();
-    uint32 nUpdateInterval = 5 * 60 * 1000;
-    switch (eUuidDequeState)
-    {
-    case EUuidPoolState_Empty:
-        nUpdateInterval = 1 * 60 * 1000;
-        break;
-    case EUuidPoolState_Not_Full_0_30_Per:
-        nUpdateInterval = 2 * 60 * 1000;
-        break;
-    case EUuidPoolState_Not_Full_30_70_Per:
-        nUpdateInterval = 3 * 60 * 1000;
-        break;
-    case EUuidPoolState_Not_Full_70_100_Per:
-        nUpdateInterval = 4 * 60 * 1000;
-        break;
-    case EUuidPoolState_Full:
-        nUpdateInterval = 5 * 60 * 1000;
-        break;
-    default:
-        nUpdateInterval = 5 * 60 * 1000;
-        break;
-    }
-
-    EXLOG_DEBUG << "[RyzUuid]CUUIDPool::OnTimer Now EUuidPoolState : " << eUuidDequeState;
-    SetTimer(eUuidDequeState, nUpdateInterval, nUpdateInterval, ETIMER_ONCE);
-}
-
-

参考文章

分布式唯一id:snowflake算法思考 - 掘金
https://tech.meituan.com/2017/04/21/mt-leaf.html
GitHub - baidu/uid-generator: UniqueID generator

- -
-
-
-
- -
- - - - - - -
- - - - - - -
- - - -
-
- - \ No newline at end of file diff --git "a/2020/01/23/\345\215\225\347\224\237\344\272\247\350\200\205\345\215\225\346\266\210\350\264\271\350\200\205\347\216\257\345\275\242\347\274\223\345\206\262/index.html" "b/2020/01/23/\345\215\225\347\224\237\344\272\247\350\200\205\345\215\225\346\266\210\350\264\271\350\200\205\347\216\257\345\275\242\347\274\223\345\206\262/index.html" deleted file mode 100644 index 6c2e7e0..0000000 --- "a/2020/01/23/\345\215\225\347\224\237\344\272\247\350\200\205\345\215\225\346\266\210\350\264\271\350\200\205\347\216\257\345\275\242\347\274\223\345\206\262/index.html" +++ /dev/null @@ -1,607 +0,0 @@ - - - - - - - -单生产者单消费者环形缓冲 - Genge 随笔 - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - -
-
-
-

单生产者单消费者环形缓冲

- -
-
-

头文件

-
#ifndef _SIMPLE_LOOP_BUFFER_H__
-#define _SIMPLE_LOOP_BUFFER_H__
-
-#include "gntype.h"
-#include "gnmutex.h"
-#include "gnlock.h"
-#include "server_define.h"
-using namespace Storm;
-
-typedef struct _tagBlock
-{
-    _tagBlock() 
-    {
-        memset(pBuffer, 0, sizeof(char)*(MAX_CACHE_PACKET_SIZE + 1));
-    }
-    ~_tagBlock() {};
-
-    char  pBuffer[MAX_CACHE_PACKET_SIZE + 1]; 
-    char* GetBuff() { return pBuffer; }
-    int32 GetBuffLen() { return MAX_CACHE_PACKET_SIZE + 1; }
-}CBlock;
-
-class CSimpleLoopBufferEx
-{
-public:
-    CSimpleLoopBufferEx();
-    ~CSimpleLoopBufferEx();
-
-    void Clear();
-
-    /**
-    * @brief
-    * 初始化循环缓冲区
-    * @param nSize : 初始化虚幻缓冲区的大小,实际大小为nSize+1
-    * @return 成功返回true,失败返回false
-    */
-    bool Init();
-
-    /**
-    * @brief
-    * 将需要存储的Buffer拷贝到循环缓冲区的结尾
-    * @param pData : [输入参数]指向需要插入循环缓冲区的Buffer起始位置
-    * @param nLen : 指向需要插入的Buffer的长度
-    * @return 如果循环缓冲区拥有的大小大于等于nLen,返回true,否则返回false
-    * @remark 此函数不是线程安全的
-    */
-    INT32 PushBack(const CHAR *pData, INT32 nLen);
-
-    /**
-    * @brief
-    * 从循环缓冲区的起始位置取nLen长度的Buffer,拷贝放入Buffer中
-    * @param pBuf : [输入输出参数]获取数据的Buffer的起始指针
-    * @param nLen : 需要读出的Buffer长度
-    * @return 如果有足够所需读出的数据,返回true,否则返回false
-    * @remark 此函数不是线程安全的
-    */
-    INT32 PopFront(CHAR * &pBuf, INT32 nLen,CHAR* szData);
-
-    /**
-    * @brief
-    * 丢弃nLen长度的数据
-    * @param nLen : 需要丢弃的长度
-    * @return void
-    * @remark 此函数不是线程安全的
-    */
-    bool DiscardFront(INT32 nLen);
-
-    /**
-    * @brief
-    * 获取剩余可用空间大小
-    * @return INT32
-    * @remark 此函数不是线程安全的
-    */
-    INT32 GetFreeSpare();
-
-    /**
-    * @brief
-    * 拷贝内存中的数据
-    * @param nReadOffSet : m_pNextRead的偏移量
-    * @param nLen : 需要读出的Buffer长度
-    * @remark 此函数不是线程安全的
-    */
-    INT32 GetData(INT32 nReadOffSet,INT32 nLen, CHAR* szData);
-
-
-private:
-    CHAR    *m_pBuffer;
-    CHAR    *m_pNextRead;
-    CHAR    *m_pNextWrite;
-    CHAR    *m_pEnd;
-};
-
-#endif
- -

实现

-
#include "simpleloopbuffer.h"
-#include <stdio.h>
-#include <memory.h>
-#include "gndebug.h"
-#include "../../common/commonloggerex.h"
-#include "gate_factory.h"
-
-
-using namespace Storm;
-
-CSimpleLoopBufferEx::CSimpleLoopBufferEx() :
-    m_pBuffer(NULL),
-    m_pNextRead(NULL),
-    m_pNextWrite(NULL),
-    m_pEnd(NULL)
-{
-}
-
-CSimpleLoopBufferEx::~CSimpleLoopBufferEx()
-{
-    if (NULL != m_pBuffer)
-    {
-        CGateFactory::Instance()->ReleaseBlock((CBlock*)m_pBuffer);
-        m_pBuffer = NULL;
-        //delete[] m_pBuffer;
-        //m_pBuffer = NULL;
-    }
-}
-
-void CSimpleLoopBufferEx::Clear()
-{
-    m_pNextRead = m_pBuffer;
-    m_pNextWrite = m_pBuffer;
-}
-
-bool CSimpleLoopBufferEx::Init()
-{
-    //m_pBuffer = SDNew char[nSize + 1];
-    CBlock* pBlock = CGateFactory::Instance()->CreateBlock();
-    if (!pBlock)
-        return false;
-
-    m_pBuffer = pBlock->GetBuff();
-    if (NULL == m_pBuffer)
-    {
-        return false;
-    }
-    m_pNextRead = m_pBuffer;
-    m_pNextWrite = m_pBuffer;
-    //m_pEnd = m_pBuffer + nSize + 1;
-    m_pEnd = m_pBuffer + pBlock->GetBuffLen();
-    return true;
-}
-
-INT32 CSimpleLoopBufferEx::PushBack(const CHAR *pData, INT32 nLen)
-{
-    CHAR* poRead = m_pNextRead;
-    if (m_pNextWrite >= poRead)
-    {
-        //  1、尾部指针减去写起始位置小于 nLen 
-        //  2、读的起始位置减去内存首地址 小于nLen
-        //  内存空间不够,不能将数据写入缓存
-        /*                          == 内存模型 ==
-                   (empty)             m_pNextRead         m_pNextWrite       (empty)
-        |----------------------------------|--------------------|---------------------|
-        */
-        INT32 nRight = m_pEnd - m_pNextWrite;
-        INT32 nLeft = poRead - m_pBuffer;
-        if (nLeft + nRight <= nLen)
-        {
-            return -1;
-        }
-    }
-    else
-    {
-        /*                          == 内存模型 ==
-                                       m_pNextWrite (empty) m_pNextRead
-        |----------------------------------|--------------------|---------------------|
-        */
-        if (poRead - m_pNextWrite <= nLen)
-        {
-            return -2;
-        }
-    }
-    //     else  //  m_pNextWrite == m_pNextRead
-    //     {
-    //         if (m_pNextWrite > m_pBuffer)
-    //         {
-    //             m_oMutex.Unlock();
-    //             return -3;
-    //         }
-    //     }
-    if (m_pEnd - m_pNextWrite > nLen)
-    {
-        memcpy(m_pNextWrite, pData, nLen);
-        m_pNextWrite += nLen;
-    }
-    else if (m_pEnd - m_pNextWrite == nLen)
-    {
-        memcpy(m_pNextWrite, pData, nLen);
-        m_pNextWrite = m_pBuffer;
-    }
-    else
-    {
-        INT32 nStartLen = m_pEnd - m_pNextWrite;
-        memcpy(m_pNextWrite, pData, nStartLen);
-        memcpy(m_pBuffer, pData + nStartLen, nLen - nStartLen);
-        m_pNextWrite = m_pBuffer + nLen - nStartLen;
-    }
-    return nLen;
-}
-
-INT32 CSimpleLoopBufferEx::PopFront(CHAR * &pBuf, INT32 nLen, CHAR* szData)
-{
-    CHAR* poNextWrite = m_pNextWrite;
-    if (poNextWrite == m_pNextRead)
-    {
-        EXLOG_ERROR << "[%s:%d]:PopFront failed! ErrCode:-1";
-        return -1;
-    }
-    if (poNextWrite > m_pNextRead)
-    {
-        /*                          == 内存模型 ==
-                   (empty)             m_pNextRead         m_pNextWrite       (empty)
-        |----------------------------------|--------------------|---------------------|
-        */
-        if (poNextWrite - m_pNextRead < nLen)
-        {
-            EXLOG_ERROR << "PopFront failed! ErrCode:-2";
-            return -2;
-        }
-    }
-    else
-    {
-        /*                          == 内存模型 ==
-                                      m_pNextWrite (empty) m_pNextRead
-        |----------------------------------|--------------------|---------------------|
-        */
-        INT32 nRight = m_pEnd - m_pNextRead;
-        INT32 nLeft = poNextWrite - m_pBuffer;
-        if (nLeft + nRight < nLen)
-        {
-            EXLOG_ERROR << "[%s:%d]:PopFront failed! ErrCode:-3";
-            return -3;
-        }
-    }
-    if (m_pEnd - m_pNextRead > nLen)
-    {
-        memcpy(szData, m_pNextRead, nLen);
-        m_pNextRead += nLen;
-    }
-    else if (m_pEnd - m_pNextRead == nLen)
-    {
-        memcpy(szData, m_pNextRead, nLen);
-        m_pNextRead = m_pBuffer;
-    }
-    else
-    {
-        INT32 nStartLen = m_pEnd - m_pNextRead;
-        memcpy(szData, m_pNextRead, nStartLen);
-        memcpy(szData + nStartLen, m_pBuffer, nLen - nStartLen);
-        m_pNextRead = m_pBuffer + nLen - nStartLen;
-    }
-    return nLen;
-}
-
-
-bool CSimpleLoopBufferEx::DiscardFront(INT32 nLen)
-{
-    CHAR* poNextWrite = m_pNextWrite;
-    if (poNextWrite == m_pNextRead)
-    {
-        EXLOG_ERROR << "[%s:%d]:PopFront failed! ErrCode:-1";
-        return false;
-    }
-    if (poNextWrite > m_pNextRead)
-    {
-        /*                          == 内存模型 ==
-        (empty)             m_pNextRead         m_pNextWrite       (empty)
-        |----------------------------------|--------------------|---------------------|
-        */
-        if (poNextWrite - m_pNextRead < nLen)
-        {
-            EXLOG_ERROR << "[%s:%d]:PopFront failed! ErrCode:-2";
-            return false;
-        }
-    }
-    else
-    {
-        /*                          == 内存模型 ==
-        m_pNextWrite (empty) m_pNextRead
-        |----------------------------------|--------------------|---------------------|
-        */
-        INT32 nRight = m_pEnd - m_pNextRead;
-        INT32 nLeft = poNextWrite - m_pBuffer;
-        if (nLeft + nRight < nLen)
-        {
-            EXLOG_ERROR << "[%s:%d]:PopFront failed! ErrCode:-3";
-            return false;
-        }
-    }
-    if (m_pEnd - m_pNextRead > nLen)
-    {
-        m_pNextRead += nLen;
-    }
-    else if (m_pEnd - m_pNextRead == nLen)
-    {
-        m_pNextRead = m_pBuffer;
-    }
-    else
-    {
-        INT32 nStartLen = m_pEnd - m_pNextRead;
-        m_pNextRead = m_pBuffer + nLen - nStartLen;
-    }
-
-    return true;
-}
-
-INT32 CSimpleLoopBufferEx::GetFreeSpare()
-{
-    CHAR* poRead = m_pNextRead;
-    if (m_pNextWrite >= poRead)
-    {
-        //  1、尾部指针减去写起始位置小于 nLen 
-        //  2、读的起始位置减去内存首地址 小于nLen
-        //  内存空间不够,不能将数据写入缓存
-        /*                          == 内存模型 ==
-        (empty)             m_pNextRead         m_pNextWrite       (empty)
-        |----------------------------------|--------------------|---------------------|
-        */
-        INT32 nRight = m_pEnd - m_pNextWrite;
-        INT32 nLeft = poRead - m_pBuffer;
-        return nLeft + nRight;
-
-    }
-    else
-    {
-        /*                          == 内存模型 ==
-        m_pNextWrite (empty) m_pNextRead
-        |----------------------------------|--------------------|---------------------|
-        */
-
-        return poRead - m_pNextWrite;
-    }
-}
-
-INT32 CSimpleLoopBufferEx::GetData(INT32 nReadOffSet, INT32 nLen, CHAR* szData)
-{
-    //指针偏移
-    CHAR* poNextWrite = m_pNextWrite;
-    if (poNextWrite == m_pNextRead)
-    {
-        EXLOG_ERROR << "[%s:%d]:PopFront failed! ErrCode:-1";
-        return -1;
-    }
-
-    CHAR *pReadOffSet = m_pNextRead + nReadOffSet;
-    if (poNextWrite > m_pNextRead)
-    {
-        /*                          == 内存模型 ==
-        (empty)             m_pNextRead         m_pNextWrite       (empty)
-        |----------------------------------|--------------------|---------------------|
-        */
-
-        if (pReadOffSet >= m_pNextWrite)
-        {
-            EXLOG_ERROR << "GetData failed! ErrCode:-2";
-            return -2;
-        }
-    }
-    else
-    {
-        /*                          == 内存模型 ==
-        m_pNextWrite (empty) m_pNextRead
-        |----------------------------------|--------------------|---------------------|
-        */
-
-        if (pReadOffSet < m_pEnd)
-        {
-
-        }
-        else if (pReadOffSet == m_pEnd)
-        {
-            pReadOffSet = m_pBuffer;
-            if (pReadOffSet >= m_pNextWrite)
-            {
-                EXLOG_ERROR << "GetData failed! ErrCode:-3";
-                return -3;
-            }
-        }
-        else {
-            pReadOffSet = m_pBuffer + nReadOffSet - (m_pEnd - m_pNextRead);
-            if (pReadOffSet >= m_pNextWrite)
-            {
-                EXLOG_ERROR << "GetData failed! ErrCode:-4";
-                return -4;
-            }
-        }
-    }
-
-    //拷贝数据
-
-    if (poNextWrite > pReadOffSet)
-    {
-        /*                          == 内存模型 ==
-        (empty)             m_pNextRead         m_pNextWrite       (empty)
-        |----------------------------------|--------------------|---------------------|
-        */
-        if (poNextWrite - pReadOffSet < nLen)
-        {
-            EXLOG_ERROR << "PopFront failed! ErrCode:-5";
-            return -5;
-        }
-    }
-    else
-    {
-        /*                          == 内存模型 ==
-        m_pNextWrite (empty) m_pNextRead
-        |----------------------------------|--------------------|---------------------|
-        */
-        INT32 nRight = m_pEnd - pReadOffSet;
-        INT32 nLeft = poNextWrite - m_pBuffer;
-        if (nLeft + nRight < nLen)
-        {
-            EXLOG_ERROR << "[%s:%d]:PopFront failed! ErrCode:-6";
-            return -6;
-        }
-    }
-    if (m_pEnd - pReadOffSet > nLen)
-    {
-        memcpy(szData, pReadOffSet, nLen);
-        //pReadOffSet += nLen;
-    }
-    else if (m_pEnd - pReadOffSet == nLen)
-    {
-        memcpy(szData, pReadOffSet, nLen);
-        //pReadOffSet = m_pBuffer;
-    }
-    else
-    {
-        INT32 nStartLen = m_pEnd - pReadOffSet;
-        memcpy(szData, pReadOffSet, nStartLen);
-        memcpy(szData + nStartLen, m_pBuffer, nLen - nStartLen);
-        //m_pNextRead = m_pBuffer + nLen - nStartLen;
-    }
-
-
-    return nLen;
-
-}
-
-
-
-
-
- -
- - - - - - -
- - - - - - -
- - - -
-
- - \ No newline at end of file diff --git "a/2020/01/23/\345\220\204\347\261\273APP\346\216\222\350\241\214\346\246\234\345\256\236\347\216\260/index.html" "b/2020/01/23/\345\220\204\347\261\273APP\346\216\222\350\241\214\346\246\234\345\256\236\347\216\260/index.html" deleted file mode 100644 index a60b950..0000000 --- "a/2020/01/23/\345\220\204\347\261\273APP\346\216\222\350\241\214\346\246\234\345\256\236\347\216\260/index.html" +++ /dev/null @@ -1,270 +0,0 @@ - - - - - - - -各类APP排行榜实现 - Genge 随笔 - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - -
-
-
-

各类APP排行榜实现

- -
-
-

需求背景

    -
  1. 查看TopN的用户排名
  2. -
  3. 查看自己的排名
  4. -
  5. 用户积分变更后,排名及时更新
  6. -
-

实现方案

方案一 利用MYSQL 排序

利用MySQL来实现,存放一张用户积分表user_score
取前top N,自己的排名都可以通过简单的sql语句搞定。
算法简单,利用sql的功能,不需要其他复杂逻辑,对于数据量比较少、性能要求不高,可以使用。但是对于海量数据,性能是无法接受的。可能会导致全局锁表之类的问题。

-

方案二 内存数组排序

在内存中预分配所要排名用户大小的数组,所有的积分排名变更基于此移动元素,成熟排序算法有最小/大堆、快速排序等,这种方案优点是当数据量小的时候,简单快捷,容易实现,不需要其他任何组件支持,但是当面对海量数据的时候数组空间占用可能不太现实

-

方案三 利用GCC库支持

具体的,用GCC的pb_ds库中有assoc_container来进行实现。
参考tree_order_statistics.cc

-
#include <cassert>
-#include <ext/pb_ds/assoc_container.hpp>
-#include <ext/pb_ds/tree_policy.hpp>
-
-using namespace std;
-using namespace pb_ds;
-using namespace pb_ds;
-
-// A red-black tree table storing ints and their order
-// statistics. Note that since the tree uses
-// tree_order_statistics_node_update as its update policy, then it
-// includes its methods by_order and order_of_key.
-typedef
-tree<
-  int,
-  null_mapped_type,
-  less<int>,
-  rb_tree_tag,
-  // This policy updates nodes' metadata for order statistics.
-  tree_order_statistics_node_update>
-set_t;
-
-int main()
-{
-  // Insert some entries into s.
-  set_t s;
-  s.insert(12);
-  s.insert(505);
-  s.insert(30);
-  s.insert(1000);
-  s.insert(10000);
-  s.insert(100);
-
-  // The order of the keys should be: 12, 30, 100, 505, 1000, 10000.
-  assert(*s.find_by_order(0) == 12);
-  assert(*s.find_by_order(1) == 30);
-  assert(*s.find_by_order(2) == 100);
-  assert(*s.find_by_order(3) == 505);
-  assert(*s.find_by_order(4) == 1000);
-  assert(*s.find_by_order(5) == 10000);
-  assert(s.find_by_order(6) == s.end());
-
-  // The order of the keys should be: 12, 30, 100, 505, 1000, 10000.
-  assert(s.order_of_key(10) == 0);
-  assert(s.order_of_key(12) == 0);
-  assert(s.order_of_key(15) == 1);
-  assert(s.order_of_key(30) == 1);
-  assert(s.order_of_key(99) == 2);
-  assert(s.order_of_key(100) == 2);
-  assert(s.order_of_key(505) == 3);
-  assert(s.order_of_key(1000) == 4);
-  assert(s.order_of_key(10000) == 5);
-  assert(s.order_of_key(9999999) == 6);
-
-  // Erase an entry.
-  s.erase(30);
-
-  // The order of the keys should be: 12, 100, 505, 1000, 10000.
-  assert(*s.find_by_order(0) == 12);
-  assert(*s.find_by_order(1) == 100);
-  assert(*s.find_by_order(2) == 505);
-  assert(*s.find_by_order(3) == 1000);
-  assert(*s.find_by_order(4) == 10000);
-  assert(s.find_by_order(5) == s.end());
-
-  // The order of the keys should be: 12, 100, 505, 1000, 10000.
-  assert(s.order_of_key(10) == 0);
-  assert(s.order_of_key(12) == 0);
-  assert(s.order_of_key(100) == 1);
-  assert(s.order_of_key(505) == 2);
-  assert(s.order_of_key(707) == 3);
-  assert(s.order_of_key(1000) == 3);
-  assert(s.order_of_key(1001) == 4);
-  assert(s.order_of_key(10000) == 4);
-  assert(s.order_of_key(100000) == 5);
-  assert(s.order_of_key(9999999) == 5);
-
-  return 0;
-}
-

存取效率都可以达到O(log(n)),不足就是程序重启后数据会丢失。还是对所有的用户积分,没必要。
而且是有依赖,不方便扩展,实现不了复杂的需求,

-

方案四 实现排序树

大致实现思路如下:

-

  我们可以把[0, 1,000,000)作为一级区间;再把一级区间分为两个2级区间[0, 500,000), [500,000, 1,000,000),然后把二级区间二分为4个3级区间[0, 250,000), [250,000, 500,000), [500,000, 750,000), [750,000, 1,000,000),依此类推,最终我们会得到1,000,000个21级区间[0,1), [1,2) … [999,999, 1,000,000)。这实际上是把区间组织成了一种平衡二叉树结构,根结点代表一级区间,每个非叶子结点有两个子结点,左子结点代表低分区间,右子结点代表高分区间。树形分区结构需要在更新时保持一种不变量,非叶子结点的count值总是等于其左右子结点的count值之和。

-

  以后,每次用户积分有变化所需要更新的区间数量和积分变化量有关系,积分变化越小更新的区间层次越低。总体上,每次所需要更新的区间数量是用户积分变量的log(n)级别的,也就是说如果用户积分一次变化在百万级,更新区间的数量在二十这个级别。在这种树形分区积分表的辅助下查询积分为s的用户排名,实际上是一个在区间树上由上至下、由粗到细一步步明确s所在位置的过程。比如,对于积分499,000,我们用一个初值为0的排名变量来做累加;首先,它属于1级区间的左子树[0, 500,000),那么该用户排名应该在右子树[500,000, 1,000,000)的用户数count之后,我们把该count值累加到该用户排名变量,进入下一级区间;其次,它属于3级区间的[250,000, 500,000),这是2级区间的右子树,所以不用累加count到排名变量,直接进入下一级区间;再次,它属于4级区间的…;直到最后我们把用户积分精确定位在21级区间[499,000, 499,001),整个累加过程完成,得出排名!

-

  虽然,本算法的更新和查询都涉及到若干个操作,但如果我们为区间的from_score和to_score建立索引,这些操作都是基于键的查询和更新,不会产生表扫描,因此效率更高。另外,本算法并不依赖于关系数据模型和SQL运算,可以轻易地改造为NoSQL等其他存储方式,而基于键的操作也很容易引入缓存机制进一步优化性能。进一步,我们可以估算一下树形区间的数目大约为2,000,000,考虑每个结点的大小,整个结构只占用几十M空间。所以,我们完全可以在内存建立区间树结构,并通过user_score表在O(n)的时间内初始化区间树,然后排名的查询和更新操作都可以在内存进行。一般来讲,同样的算法,从数据库到内存算法的性能提升常常可以达到10^5以上;因此,本算法可以达到非常高的性能。

-

  算法特点

-

  优点:结构稳定,不受积分分布影响;每次查询或更新的复杂度为积分最大值的O(log(n))级别,且与用户规模无关,可以应对海量规模;不依赖于SQL,容易改造为NoSQL或内存数据结构。

-

  缺点:算法相对更复杂。

-

方案五 实现跳表排序

skip list是链表的一种特殊形式,对链表的一种优化;保证INSERT和REMOVE操作是O(logn),而通用链表的复杂度为O(n);
优点:实现较简单,效率基本上O(log(N))
缺点:当达到亿级别时的数据时,性能会急剧下降

-

方案六 利用redis特新实现

其实redis底层还是使用跳表实现排序的,只是将接口都封装好了,使用接口也比较完善,稳定。

-

redis的zset天生是用来做排行榜的、好友列表, 去重, 历史记录等业务需求。接口使用非常简单。接口非常丰富,基本上需要的实现都能满足,说明如下:

-

ZAdd/ZRem是O(log(N)),ZRangeByScore/ZRemRangeByScore是O(log(N)+M),N是Set大小,M是结果/操作元素的个数。

-

ZSET的实现用到了两个数据结构:hash table 和 skip list(跳跃表),其中hash table是具体使用redis中的dict来实现的,主要是为了保证查询效率为O(1) ,而skip list(跳跃表)主要是保证元素有序并能够保证INSERT和REMOVE操作是O(logn)的复杂度。

-

优点:基于redis开发,速度快;使用redis相关特性

-

缺点:当达到亿级别时的数据时,性能会急剧下降

-

来实现排行榜的方法很多,可以根据自己的具体需求,参考选用。

-

方案七 其实只需要TopN的排名,大于N的排名并不需要精确排名计算

基于此,假设我们游戏内只需要排前100名,这里我们只需要维护一个100大小的数组

-
    -
  1. 当元素A需要参与排序的时候,与数组中最小的积分进行比较,如果能进100名,那么将第100剔除,将A加入,并记录最小元素,这样就完成了积分上涨的情况
  2. -
  3. 还有一种就是已经在前100名中元素的积分发生变化下降,那么需要在前100名后找出可以进排行榜的元素,这种情况比较麻烦,可以使用最大堆保存剩下所有用户的数据,当需要找出能替换进入排行榜的元素就非常快logn,选择特定数据结构非常重要
  4. -
  5. 既然大于N的用户不需要精确排名,那么怎么样估算大概排名呢?一般做法是按照数值区间建立所若干个桶,比如我们预计要排名的那一个数据的最大值能到1W。我建立0-10, 10-100,100-1000, 1000-2000, 2000-5000, 5000-10000 这样6个桶,每个桶里面记录分值在这个桶对应的区间内,有多少个玩家。 比如
    0-10, 10人
    10-100,20人
    100-1000,30人
    1000-2000, 40人
    2000-5000, 50人
    5000-10000, 60人
    那么如果一个玩家 是 1234分,那么他的排名就超过了 (10 + 20 + 30)/ (10 + 20 + 30 + 40 + 50 + 60)这个百分比的玩家(所以桶分的越细,后面的排名越精确)
    实质就是按照分区间记录区间内元素个数,从而估算大概排名,因此数值区间越小,估算约精确。
  6. -
- -
-
-
-
- -
- - - - - - -
- - - - - - -
- - - -
-
- - \ No newline at end of file diff --git "a/2020/01/23/\346\267\261\345\205\245\346\265\205\345\207\272\346\227\266\351\227\264\350\275\256Timer\345\256\232\346\227\266\345\231\250/index.html" "b/2020/01/23/\346\267\261\345\205\245\346\265\205\345\207\272\346\227\266\351\227\264\350\275\256Timer\345\256\232\346\227\266\345\231\250/index.html" deleted file mode 100644 index f5d07c1..0000000 --- "a/2020/01/23/\346\267\261\345\205\245\346\265\205\345\207\272\346\227\266\351\227\264\350\275\256Timer\345\256\232\346\227\266\345\231\250/index.html" +++ /dev/null @@ -1,644 +0,0 @@ - - - - - - - -Timer 定时器技术分享 - Genge 随笔 - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - -
-
-
-

Timer 定时器技术分享

- -
-
-

说点废话

-

不管是客户端Client还是服务器Server,不论你是从事游戏行业还是互联网行业,在技术上总会涉及到定时器。虽然有的框架系统已经帮你实现,并且提供完美API供你使用,但你真的了解定时器吗?我们不仅要知道如何使用正确的Timer,还得明白定时器的实现原理,要知其所以然。

-
-

理解定时器

使用者角度分类:

-
    -
  1. 周期性定时器

    -
      -
    1. 使用 TCP 长连接时,客户端需要定时向服务端发送心跳请求
    2. -
    3. 游戏内系统每日重置功能
    4. -
    5. 体力回复
    6. -
    7. ….
    8. -
    -
  2. -
  3. 非周期性定时器

    -
      -
    1. 玩法活动定时开启、关闭
    2. -
    3. -
    -
  4. -
-

当然,大部分非周期性定时器都可以使用周期性定时器实现,即执行一次后立即调用Remove接口即可

-

定时器像水和空气一般,普遍存在于各个场景中,一般定时任务的形式表现为:经过固定时间后触发、按照固定频率周期性触发、在某个时刻触发。定时器是什么?可以理解为这样一个数据结构:存储一系列的任务集合,并且 Deadline 越接近的任务,拥有越高的执行优先级

-

支持以下几种操作:

-
    -
  1. Add New TimerTask 添加新的定时器
  2. -
  3. Kill Or Remove TimerTask 取消或者移除既有定时器任务
  4. -
  5. Run 执行
  6. -
-

判断一个TimerTask是否到期,基本会采用轮询的方式,每隔一个时间片tickDuration去检查最近的任务是否到期。

-
-

说到底,定时器还是靠线程轮询实现的。

-
-

现在知道Timer是靠轮询来实现的,那么中间应该采用那种数据结构呢?采用不同的数据结构实现,其性能也大不一样!
现在主要有如下几种:List链表、Heap最小堆、时间轮、分级时间轮,其中时间轮的实质为Hash表。

-

数据结构

双向有序链表

AddTimer O(N) 很容易理解,按照 expireTime 查找合适的位置即可;KillTimer O(1) ,任务在 Kill 时,会持有自己节点的引用,所以不需要查找其在链表中所在的位置,即可实现当前节点的删除;RunTimer O(1),由于整个双向链表是基于 expireTime 有序的,所以调度器只需要轮询第一个任务即可。

-

最小堆

最小堆指的是满足除了根节点以外的每个节点都不小于其父节点的堆。这样,堆中的最小值就存放在根节点中,并且在以某个结点为根的子树中,各节点的值都不小于该子树根节点的值。一个最小堆的例子如下图:
最小堆

-

明显的,最小堆添加新元素或者删除节点效率为O(lgn), root节点expireTime最小,执行优先级最高,因此复杂度为O(1)

-

如果程序中的定时器数量比较少,基于最小堆的定时器一般可以满足需求,且实现简单。

-

时间轮

时间轮的实质为哈希环HashTable,每个定时器任务根据对其expireTime哈希,得到对应的位置index,复杂度为O(1)
时间轮

-

性能比较:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
实现方式AddTimerKillTimerRunTimer
基于链表O(1)O(n)O(n)
基于排序链表O(n)O(1)O(1)
基于最小堆O(lgn)O(lgn)O(1)
基于时间轮O(1)O(1)O(1)
-

现在看起来我们选择时间轮来实现就行了,是否这样就完事了?

-

着重分析时间轮

如果需要支持的定时器范围非常的大,上面的实现方式则不能满足这样的需求。因为这样将消耗非常可观的内存,假设需要表示的定时器范围为:0 – 2^3-1ticks,则简单时间轮需要 2^32 个元素空间,这对于内存空间的使用将非常的庞大。也许可以降低定时器的精度,使得每个 Tick 表示的时间更长一些,但这样的代价是定时器的精度将大打折扣。

-

现在的问题是,度量定时器的粒度,只能使用唯一粒度吗?想想日常生活中常遇到的水表,如下图:
水表

-

钟表:
水表

-

分级时间轮同样如此,每级时间轮所代表的粒度精度都不一样,这样结合起来,既能够保证定时器的精度,也能以较小内存代价表示范围更大更多的定时器。

-

简单时间轮: 一个齿轮,每个齿轮保存一个超时的node链表。一个齿轮表示一个时间刻度,比如钟表里面一小格代表一秒,钟表的秒针每次跳一格。假设一个刻度代表10ms,则2^32 个格子可表示1.36年,2^16个格子可表示10.9分钟。当要表示的时间范围较大时,空间复杂度会大幅增加。

-

分级时间轮: 类似于水表,当小轮子里的指针转动满一圈后,上一级轮子的指针进一格。 采用五个轮子每个轮子为一个简单时间轮,大小分别为 2^8, 2^6, 2^6, 2^6, 2^6,所需空间:2^8 + 2^6 + 2^6 + 2^6 + 2^6 = 512, 可表示的范围为 0 – 2^8 * 2^6 * 2^6* 2^6* 2^6 = 2^32 。

-

分级时间轮简洁图
分级时间轮

-

熟知的Linux系统内核,定时器实现方式就是分级时间轮
Linux内核

-

具体实现

wheel_timer_mgr.h

-
class CWheelTimerModule;
-class ITimerMgr;
-class CWheelTimer;
-
-typedef std::list<CWheelTimer*> TListTimer;
-
-enum ETimerType 
-{ 
-    ETIMER_ONCE, 
-    ETIMER_CIRCLE 
-};
-
-///定时器最小精度 1/10秒 (100毫秒)
-static const int  WHEEL_TIMER_MIN_PRECISION = 100;
-
-/**
-* @brief 基于分级的时间轮定时器, 精度约定为十分之一秒
-* 注意,注册的Timer精度必须为约定的精度倍数
-*/
-class CWheelTimer
-{
-public:
-    CWheelTimer();
-    CWheelTimer(CWheelTimerModule& oModule);
-    ~CWheelTimer();
-
-    /**
-    * @brief 启动定时器
-    * @param nInterval: 传入的是毫秒, 约定必须是十分之一秒(100ms)的倍数
-    * @return 
-    */
-    void Start(ITimerMgr* pITimer, unsigned int nId, unsigned nInterval, int nDelay, ETimerType eTimerType = ETIMER_CIRCLE);
-
-    /**
-    * @brief 停止定时器
-    */
-    void Stop();
-private:
-    /**
-    * @brief 定时器被触发
-    */
-    void OnTrigger(const UINT64 nNow);
-
-private:
-    friend class CWheelTimerModule;
-
-    CWheelTimerModule&		m_oModule;
-    ITimerMgr*				m_pTimerMgr = nullptr;
-    ETimerType				m_eTimerType;
-    unsigned int			m_nTimerId = 0;
-    unsigned int			m_nInterval = -1;    //ms
-    unsigned long long		m_llExpireTime = 0;  //ms
-    int						m_nVecIndex = 0;
-    TListTimer::iterator	m_listIter;
-};
-
-/**
-* @brief 定时器管理器接口, 派生类继承使用
-*/
-class ITimerMgr
-{
-public:
-    virtual		  ~ITimerMgr();
-    virtual void  OnTimer(unsigned int nId) = 0;
-    //interval 时间精度 ms
-    void		  SetTimer(unsigned int nId, int nInterval, int nDelay = 0, ETimerType eTimeType = ETIMER_CIRCLE);
-    void		  KillTimer(unsigned int nId);
-    bool		  IsTimerExist(unsigned int nId);;
-
-protected:
-    std::unordered_map<unsigned int, CWheelTimer*> m_mapTimer;
-};
-
-/**
-* @brief 全局定时器管理模块, 负责管理所有的定时器
-*/
-class CWheelTimerModule : public Storm::TSingleton<CWheelTimerModule>
-{
-    friend class Storm::TSingleton<CWheelTimerModule>;
-    CWheelTimerModule();
-public:
-    void			AddTimer(CWheelTimer* pTimer);
-    void			RemoveTimer(CWheelTimer* pTimer);
-    /**
-    * @brief 驱动所有的定时器
-    */
-    void			Run();
-
-    static UINT64	GetCurMillisecs();
-    /**
-    * @brief 修正精度 
-    * @param nSrcTime 
-    * @return 传入的时间除以当前约定最小定时器精度
-    */
-    static UINT64	HandlePrecision(const UINT64 nSrcTime);
-
-private:
-    int				_Cascade(int nOffset, int nIndex);
-
-private:
-    std::vector<TListTimer>		m_vecTimerList;
-
-    //notice: precision=100ms not 1ms
-    UINT64						m_llCheckTime;
-};
-
-/**
-* @brief 定时器工厂 
-*/
-class CTimerFactory: public Storm::TSingleton<CTimerFactory>, public CNoncopy
-{
-    friend class Storm::TSingleton<CTimerFactory>;
-    CTimerFactory();
-    virtual			~CTimerFactory();
-public: 
-    CWheelTimer*	CreateCTimer();
-    void			ReleaseCTimer(CWheelTimer* pTimer);
-private:
-
-    CSTObjectPool<CWheelTimer>    m_oCTimerPool;
-}
- -

wheel_timer_mgr.cpp

-
#if 0 
-    #define TVN_BITS 6
-    #define TVR_BITS 8
-    #define TVN_SIZE (1 << TVN_BITS)
-    #define TVR_SIZE (1 << TVR_BITS)
-    #define TVN_MASK (TVN_SIZE - 1)
-    #define TVR_MASK (TVR_SIZE - 1)
-    #define OFFSET(N) (TVR_SIZE + (N) *TVN_SIZE)
-    #define INDEX(V, N) ((V >> (TVR_BITS + (N) *TVN_BITS)) & TVN_MASK)
-#else 
-    static const int TVN_BITS = 6;
-    static const int TVR_BITS = 8;
-    static const int TVN_SIZE = (1 << TVN_BITS);
-    static const int TVR_SIZE = (1 << TVR_BITS);
-    static const int TVN_MASK = (TVN_SIZE - 1);
-    static const int TVR_MASK = (TVR_SIZE - 1);
-    int OFFSET(int N) { return (TVR_SIZE + (N)*TVN_SIZE); }
-    int INDEX(unsigned long long V, int N)
-    {
-        return ((V >> (TVR_BITS + (N)*TVN_BITS)) & TVN_MASK);
-    }
-
-#endif
-
-
-
-CWheelTimer::CWheelTimer()
-    :m_oModule(CWheelTimerModule::GetInstance())
-    , m_nVecIndex(-1)
-{
-}
-
-CWheelTimer::CWheelTimer(CWheelTimerModule& oModule)
-    : m_oModule(oModule)
-    , m_nVecIndex(-1)
-{
-}
-
-CWheelTimer::~CWheelTimer()
-{
-    Stop();
-}
-
-void CWheelTimer::Start(ITimerMgr* pTimerMgr, unsigned int nId, unsigned nInterval, int nDelay, ETimerType eTimerType)
-{
-    Stop();
-
-    //时间都修正为最小精度
-    if (nInterval < WHEEL_TIMER_MIN_PRECISION)
-    {
-        nInterval = WHEEL_TIMER_MIN_PRECISION;
-    }
-
-    m_nInterval = CWheelTimerModule::HandlePrecision(nInterval);
-    m_eTimerType = eTimerType;
-    m_pTimerMgr = pTimerMgr;
-    m_nTimerId = nId;
-    m_llExpireTime = CWheelTimerModule::HandlePrecision(nDelay + CWheelTimerModule::GetCurMillisecs());
-    m_oModule.AddTimer(this);
-}
-
-
-void CWheelTimer::Stop()
-{
-    if (m_nVecIndex != -1)
-    {
-        m_oModule.RemoveTimer(this);
-        m_nVecIndex = -1;
-    }
-}
-
-void CWheelTimer::OnTrigger(const UINT64 nNow)
-{
-    if (m_eTimerType == ETIMER_CIRCLE)
-    {
-        m_llExpireTime = m_nInterval + nNow;
-        m_oModule.AddTimer(this);
-    }
-    else
-    {
-        m_nVecIndex = -1;
-    }
-
-    if (m_pTimerMgr != nullptr)
-    {
-        m_pTimerMgr->OnTimer(m_nTimerId);
-    }
-}
-
-//--------------------------------------------------------------------------------------------------------
-
-CWheelTimerModule::CWheelTimerModule()
-{
-    m_vecTimerList.resize(TVR_SIZE + 4 * TVN_SIZE);
-    m_llCheckTime = HandlePrecision(GetCurMillisecs());
-}
-
-void CWheelTimerModule::AddTimer(CWheelTimer* pTimer)
-{
-    UINT64 llExpireTime = pTimer->m_llExpireTime;
-    INT64 llTimeDiff = pTimer->m_llExpireTime - m_llCheckTime;
-
-    if (llTimeDiff < 0)
-    {
-        pTimer->m_nVecIndex = m_llCheckTime & TVR_MASK;
-    }
-    else if (llTimeDiff < TVR_SIZE)
-    {
-        pTimer->m_nVecIndex = llExpireTime & TVR_MASK;
-    }
-    else if (llTimeDiff < 1 << (TVR_BITS + TVN_BITS))
-    {
-        pTimer->m_nVecIndex = OFFSET(0) + INDEX(llExpireTime, 0);
-    }
-    else if (llTimeDiff < 1 << (TVR_BITS + 2 * TVN_BITS))
-    {
-        pTimer->m_nVecIndex = OFFSET(1) + INDEX(llExpireTime, 1);
-    }
-    else if (llTimeDiff < 1 << (TVR_BITS + 3 * TVN_BITS))
-    {
-        pTimer->m_nVecIndex = OFFSET(2) + INDEX(llExpireTime, 2);
-    }
-    else
-    {
-        if (llTimeDiff > 0xffffffffUL)
-        {
-            llTimeDiff = 0xffffffffUL;
-            llExpireTime = llTimeDiff + m_llCheckTime;
-        }
-        pTimer->m_nVecIndex = OFFSET(3) + INDEX(llExpireTime, 3);
-    }
-
-    TListTimer& listTimer = m_vecTimerList[pTimer->m_nVecIndex];
-    listTimer.push_back(pTimer);
-    pTimer->m_listIter = listTimer.end();
-    --pTimer->m_listIter;
-}
-
-void CWheelTimerModule::RemoveTimer(CWheelTimer* pTimer)
-{
-    TListTimer& listTimer = m_vecTimerList[pTimer->m_nVecIndex];
-    listTimer.erase(pTimer->m_listIter);
-}
-
-void CWheelTimerModule::Run()
-{
-    UINT64 nNow = HandlePrecision(GetCurMillisecs());
-    while (m_llCheckTime <= nNow)
-    {
-        //for every tick
-        int index = m_llCheckTime & TVR_MASK;
-        if (!index &&
-            !_Cascade(OFFSET(0), INDEX(m_llCheckTime, 0)) &&
-            !_Cascade(OFFSET(1), INDEX(m_llCheckTime, 1)) &&
-            !_Cascade(OFFSET(2), INDEX(m_llCheckTime, 2)))
-        {
-            _Cascade(OFFSET(3), INDEX(m_llCheckTime, 3));
-        }
-
-        
-        ++m_llCheckTime;
-
-        TListTimer& listTimer = m_vecTimerList[index];
-        TListTimer listTmp;
-        listTmp.splice(listTmp.end(), listTimer);
-        for (auto itr = listTmp.begin(); itr != listTmp.end(); ++itr)
-        {
-            auto* pTimer = *itr;
-            if (pTimer != nullptr)
-            {
-                pTimer->OnTrigger(nNow);
-            }
-        }
-    }
-}
-
-int CWheelTimerModule::_Cascade(int nOffset, int nIndex)
-{
-    TListTimer& listTimer = m_vecTimerList[nOffset + nIndex];
-    TListTimer listTemp;
-    listTemp.splice(listTemp.end(), listTimer);
-
-    for (auto itr = listTemp.begin(); itr != listTemp.end(); ++itr)
-    {
-        auto* pTimer = *itr;
-        if (pTimer != nullptr)
-        {
-            AddTimer(pTimer);
-        }
-    }
-
-    return nIndex;
-}
-
-UINT64 CWheelTimerModule::HandlePrecision(const UINT64 nSrcTime)
-{
-    return nSrcTime / WHEEL_TIMER_MIN_PRECISION;
-}
-
-UINT64 CWheelTimerModule::GetCurMillisecs()
-{
-    auto llCurTime = CSysTime::Instance()->GetNowMliTime();
-    return llCurTime;
-}
-
-ITimerMgr::~ITimerMgr()
-{
-    for (auto it : m_mapTimer)
-    {
-        if (it.second != nullptr)
-        {
-            it.second->Stop();
-            CTimerFactory::Instance()->ReleaseCTimer(it.second);
-        }
-    }
-    m_mapTimer.clear();
-}
-
-void ITimerMgr::SetTimer(unsigned int nId, int nInterval, int nDelay ,ETimerType eTimeType)
-{
-    if (IsTimerExist(nId))
-    {
-        EXLOG_DEBUG << "[RyzTimer]Timer Has Existed, Not Repeat Add, nId:" << nId;
-        return;
-    }
-
-    CWheelTimer* pTimer = CTimerFactory::Instance()->CreateCTimer();
-    if (nullptr == pTimer)
-    {
-        return;
-    }
-
-    m_mapTimer[nId] = pTimer;
-    pTimer->Start(this, nId, nInterval, nDelay, eTimeType);
-    EXLOG_DEBUG << "[RyzTimer]Add Timer nId:" << nId << ",nInterval:" << nInterval << ",nDelay:" << nDelay << ",eTimeType:" << eTimeType;
-}
-
-void ITimerMgr::KillTimer(unsigned int nId)
-{
-    auto it = m_mapTimer.find(nId);
-    if (it != m_mapTimer.end())
-    {
-        // 释放后 在TimerManager中 不会再执行 不需要做其他的操作
-        it->second->Stop();
-        CTimerFactory::Instance()->ReleaseCTimer(it->second);
-        m_mapTimer.erase(it);
-    }
-}
-
-bool ITimerMgr::IsTimerExist(unsigned int nId) 
-{ 
-    return m_mapTimer.find(nId) != m_mapTimer.end(); 
-}
-
-CTimerFactory::CTimerFactory()
-{
-    m_oCTimerPool.Init(32, 8);
-}
-
-CTimerFactory::~CTimerFactory()
-{
-}
-
-CWheelTimer * CTimerFactory::CreateCTimer()
-{
-    CWheelTimer* pTimer = m_oCTimerPool.FetchObj();
-    return pTimer;
-}
-
-void CTimerFactory::ReleaseCTimer(CWheelTimer* pTimer)
-{
-    if (nullptr != pTimer)
-    {
-        m_oCTimerPool.ReleaseObj(pTimer);
-    }
-}
-
-
-
-
-
-
- -
- - - - - - -
- - - - - - -
- - - -
-
- - \ No newline at end of file diff --git "a/2020/01/23/\347\275\221\346\230\223\344\272\222\345\250\261\351\235\242\350\257\225\346\200\273\347\273\223/index.html" "b/2020/01/23/\347\275\221\346\230\223\344\272\222\345\250\261\351\235\242\350\257\225\346\200\273\347\273\223/index.html" deleted file mode 100644 index c7bd13e..0000000 --- "a/2020/01/23/\347\275\221\346\230\223\344\272\222\345\250\261\351\235\242\350\257\225\346\200\273\347\273\223/index.html" +++ /dev/null @@ -1,324 +0,0 @@ - - - - - - - -网易互娱面试总结 - Genge 随笔 - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - -
-
-
-

网易互娱面试总结

- -
-
-

现场二面技术总监面试

这个人看起来挺和善的,实际上还是有一套的,让你写代码,然后眼睛编译给你指出错误,然后修改,然后他就去玩手机了…

-
    -
  1. linux env multi-thread gdb debug 多线程调试 (回答对一半)
    首先介绍下基本命令
      -
    • info threads 显示当前可以调试的所有线程,gdb会为每一个线程分配一个唯一ID,利用这个唯一Id可以切换到这个线程上下文环境中,并且前面有*标识的是当前调试的线程
    • -
    • thread ID 切换调试的线程为指定ID的线程。这个Id是gdb为每个线程分配的,并不是操作系统分配的PID,可以通过info threads命令查看
    • -
    • break xx.cpp:123 thread all 在所有线程中相应的行上设置断点
    • -
    • break apply ID1 ID2 cmd 让一个或者多个线程执行gdb命令cmd
    • -
    • break apply all cmd 让所有被调试线程执行GDB命令command
    • -
    • set print thread-events 设置线程创建提醒 当运行过程总产生新线程的时候会打印
    • -
    • set scheduler-locking off|on|step 这个是重点,经常被问到,在使用step或者continue命令调试当前被调试线程的时候,其他线程也是同时执行的,怎么只让被调试程序执行呢?通过这个命令就可以实现这个需求。
        -
      • off 不锁定任何线程,也就是所有线程都执行,这是默认值。
      • -
      • on 只有当前被调试程序会执行。.
      • -
      • step 在单步的时候,除了next过一个函数的情况(熟悉情况的人可能知道,这其实是一个设置断点然后continue的行为)以外,只有当前线程会执行。该模式是对single-stepping模式的优化。此模式会阻止其他线程在当前线程单步调试时,抢占当前线。因此调试的焦点不会被以外的改变。其他线程不可能抢占当前的调试线程。其他线程只有下列情况下会重新获得运行的机会:当你‘next’一个函数调用的时候。当你使用诸如‘continue’、‘until‘、’finish‘命令的时候。其他线程遇到设置好的断点的时候。
      • -
      -
    • -
    -
  2. -
-
    -
  • 调试C++或者C的宏
    在编译程序的时候,加上-ggdb3参数,这样就可以调试宏
      -
    • info macro – 你可以查看这个宏在哪些文件里被引用了,以及宏定义是什么样的。
    • -
    • macro – 你可以查看宏展开的样子。
    • -
    -
  • -
  • 关联源文件
      -
    • 如果在编译情况下加上-g参数,那么就可以包含debug信息,否者gdb找不到符号表
    • -
    • 可以使用directory命令来设置源文件的目录
    • -
    -
  • -
  • 条件断点
    基本语法 break [where] if [condition] 尤其是在一个循环或递归中,或是要监视某个变量。注意,这个设置是在GDB中的,只不过每经过那个断点时GDB会帮你检查一下条件是否满足。
  • -
  • 添加参数
      -
    1. gdb命令行的 –args 参数
    2. -
    3. gdb环境中 set args命令。
    4. -
    -
  • -
  • 设置变量
      -
    1. 可以直接使用set命令 设置上下文环境变量值,可以模拟一些很难在测试中出现的情况,以防未来程序出错
    2. -
    3. 声明变量,然后使用,语法为$name = 1
    4. -
    -
  • -
  • X命令
    平时我们一般使用p命令打印参数值,但是这个命令必须指定变量名,不知道变量名的时候,我们可以使用X命令
      -
    1. x\x 以十六进制输出
    2. -
    3. x\d 以十进制输出
    4. -
    5. x\c 以单字符输出
    6. -
    7. x\i 反汇编 – 通常,我们会使用 x\10i $ip-20 来查看当前的汇编($ip是指令寄存器)
    8. -
    9. x\s 以字符串输出
    10. -
    -
  • -
  • command命令 把一组命令录制下来打包成‘宏’
  • -
-
    -
  1. 循环队列判空 (OK)
    有三种方式处理这种问题

    -
      -
    1. 队列Queue结构中保存一个计数器count表示当前队列元素个数(最简单粗暴),但count等于队列cap的时候就队列满,count为0的时候队列空
    2. -
    3. 少用一个元素空间,约定以“队列头指针front在队尾指针rear的下一个位置上”作为队列“满”状态的标志。这种方法比较常用,但是面试官不让用…. front指向队首元素,rear指向队尾元素的下一个元素。即:
        -
      • 队空时: front=rear
      • -
      • 队满时: (rear+1)%maxsize=front
      • -
      -
    4. -
    5. 还有一个比较取巧的办法,优化第一种方案:使用一个状态flag变量,初始值为0,但入队成功置flag = 1,当出队成功设置flag = 0。我们可以使用 front == rear && flag 表示队列满(在入队操作之后导致front=rear),可以使用front == rear && !flag表示队列空(出队后导致f==r,显然是队列空)
    6. -
    -
  2. -
  3. 单向队列反转 (OK) 很简单

    -
  4. -
-

-struct Node
-{
-    int data;
-    Node * next;
-};
-
-Node* reverse_list(Node* head)
-{
-    if(head == nullptr) return head;
-
-    Node* node = nullptr;
-    node = head;
-    head = head->next;
-    node = nullptr;
-
-    while(head != nullptr)
-    {
-        Node* next = head->next;
-        head->next = node;
-        node = head;
-        head = next;
-    }
-
-    return node;
-}
-
- -

现场面一面回忆总结

估计是小组长之类的面试官吧,去之前我还特意看下自己的衣装是否整洁,这个面试官感觉是从工地上回来的,衣服上很脏,典型程序员面孔,他问的问题算是比较全面
操作系统(linux)、数据库(mysql)、算法、数据结构、计算机网络基础、网络编程、语言基础(C++语言)、并发、以及具体业务设计,还有项目基本介绍,游戏服务器架构简单介绍

-

常见模块实现

-
    -
  1. 定时器实现方式目前应用比较多的有时间轮和最小堆方式 , 优缺点其实就是算法复杂度:
    实现方式 StartTimer StopTimer PerTickBookkeeping
    基于链表 O(1) O(n) O(n)
    基于排序链表 O(n) O(1) O(1)
    基于最小堆 O(lgn) O(1) O(1)
    基于时间轮 O(1) O(1) O(1)
    https:\www.ibm.com\developerworks\cn\linux\l-cn-timers\

    -
  2. -
  3. 斐波那契数 多种实现

    -
      -
    1. 递归 最简单 粗暴 效率最低 存在大量重复计算
    2. -
    3. 循环叠加 算法复杂度为O(n)
    4. -
    5. 申请额外数组保存结果 去除重复计算 空间换时间
    6. -
    7. 利用数学公式推导,矩阵相乘推导公式,算法复杂度为O(logn) 效率最高
      {f(n), f(n-1), f(n-1), f(n-2)} ={1, 1, 1,0}n-1
      {f(n), f(n-1), f(n-1), f(n-2)}
      详情见 https:\www.cnblogs.com\python27\archive\2011\11\25\2261980.html
    8. -
    9. 通项公式 这个实在是牛逼 一个公式搞定… 不是数学系 这些方法确实想不出 只能记忆
        int Fibonacci(int n)
      -{
      -    double tmp=sqrt(double(5));
      -    return int((pow((1+tmp)\2,n)-pow((1-tmp)\2,n))\tmp);
      -}
      -
    10. -
    11. 定理
        int Fibonacci(int n)
      -{
      -    if(n==0)return 0;
      -    if(n==1||n==2)return 1;    \\当n>=3时,n>n\2+1
      -    int x=Fibonacci(n\2);
      -    int y=Fibonacci(n\2+1);
      -    if(n&1)return x*x+y*y;
      -    return x*(2*y-x);
      -}
    12. -
    -
  4. -
  5. 敏感字过滤算法

    -
      -
    1. 正则匹配 面试官一般不会让你用这个 因为要匹配的内容太多 写正则表达式就很烦… 效率还不高 KMP算法 太慢 不能用
    2. -
    3. 自己当时想到的一张方法为字典树TrieTree 这个方法太耗空间 时间复杂度为O(key_max_len) 很多生产环境确实是用这个实现的,别名有限状态机 DFA:DFA即Deterministic Finite Automaton,也就是确定有穷自动机
    4. -
    5. 其他什么优化算法 其实也不用
    6. -
    -
  6. -
  7. 数据库

    -
      -
    1. 数据库事务特性
      ACID 原子性 一致性 隔离性 持久性dura

      -
    2. -
    3. 事务隔离级别

      -
        隔离级别               脏读(Dirty Read)          不可重复读(NonRepeatable Read)     幻读(Phantom Read) 
      -
      -

      未提交读(Read uncommitted) 可能 可能 可能

      -

      已提交读(Read committed) 不可能 可能 可能

      -

      可重复读(Repeatable read) 不可能 不可能 可能

      -

      可串行化(Serializable ) 不可能 不可能 不可能

      -

      ·未提交读(Read Uncommitted):允许脏读,也就是可能读取到其他会话中未提交事务修改的数据(事务之间关系)
      ·提交读(Read Committed):只能读取到已经提交的数据。Oracle等多数数据库默认都是该级别 (不重复读) (事务之间关系)
      ·可重复读(Repeated Read):可重复读。在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。在SQL标准中,该隔离级别消除了不可重复读,但是还存在幻象读 (事务内部)
      ·串行读(Serializable):完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞
      可重复读和提交读是矛盾的。在同一个事务里,如果保证了可重复读,就会看不到其他事务的提交,违背了提交读;如果保证了提交读,就会导致前后两次读到的结果不一致,违背了可重复读。
      可以这么讲,InnoDB提供了这样的机制,在默认的可重复读的隔离级别里,可以使用加锁读去查询最新的数据(提交读)。
      MySQL InnoDB的可重复读并不保证避免幻读,需要应用使用加锁读来保证。而这个加锁度使用到的机制就是next-key locks。
      总结:MySQL 四种事务隔离级的说明 - jyzhou - 博客园
      四个级别逐渐增强,每个级别解决一个问题。事务级别越高,性能越差,大多数环境read committed 可以用.记住4个隔离级别的特点(上面的例子);http:\\www.cnblogs.com\zhoujinyi\p\3437475.html

      -
    4. -
    5. 事务实现原理
      https:\draveness.me\mysql-transaction 介绍事务ACID的火滚日志实现 https:\www.cnblogs.com\wy123\p\8365234.html 具体日志格式

      -
    6. -
    7. innodb和myisam存储引擎的区别 https:\blog.csdn.net\xifeijian\article\details\20316775

      -
        -
      • MyISAM类型不支持事务处理等高级处理,而InnoDB类型支持,MyISAM类型的表强调的是性能,其执行数度比InnoDB类型更快,但是不提供事务支持,而InnoDB提供事务支持以及外部键等高级数据库功能。
      • -
      • InnoDB不支持FULLTEXT类型的索引。
      • -
      • InnoDB 中不保存表的具体行数,也就是说,执行select count() from table时,InnoDB要扫描一遍整个表来计算有多少行,但是MyISAM只要简单的读出保存好的行数即可。注意的是,当count()语句包含 where条件时,两种表的操作是一样的。
      • -
      • 对于AUTO_INCREMENT类型的字段,InnoDB中必须包含只有该字段的索引,但是在MyISAM表中,可以和其他字段一起建立联合索引。
      • -
      • 细节可以看链接 https:\blog.csdn.net\xifeijian\article\details\20316775和下面一个介绍
      • -
      • https:\www.jianshu.com\p\a957b18ba40d
      • -
      -
    8. -
    9. 对于like查询啥时候会用到索引 http:\thephper.com?p=142

      -
      -

      like 不能用索引? 这个确实不知道 难受
      尽量减少like,但不是绝对不可用,”xxxx%” 是可以用到索引的,
      想象一下,你在看一本成语词典,目录是按成语拼音顺序建立,查询需求是,你想找以 “一”字开头的成语(”一%“),和你想找包含一字的成语(“%一%”)
      除了like,以下操作符也可用到索引:
      <,<=,=,>,>=,BETWEEN,IN
      <>,not in ,!=则不行

      -
      -
    10. -
    11. 索引类型
      https:\segmentfault.com\q\1010000003832312 http:\blog.codinglabs.org\articles\theory-of-mysql-index.html

      -
    12. -
    -
  8. -
- -
-
-
-
- -
- - - - - - -
- - - - - - -
- - - -
-
- - \ No newline at end of file diff --git a/2020/02/05/Flag/index.html b/2020/02/05/Flag/index.html deleted file mode 100644 index 0b6e480..0000000 --- a/2020/02/05/Flag/index.html +++ /dev/null @@ -1,166 +0,0 @@ - - - - - - - -Flag - Genge 随笔 - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - -
-
-
-

Flag

- -
-
-

重要的事情说三遍

    -
  • 做完功能要自测!!!
  • -
  • 做完功能要自测!!!
  • -
  • 做完功能要自测!!!
  • -
- -
-
-
-
- -
- - - - - - -
- - - - - - -
- - - -
-
- - \ No newline at end of file diff --git "a/2020/02/12/\346\211\271\351\207\217\346\215\242\350\241\214\347\254\246\350\275\254\346\215\242CRLF\345\210\260LF/index.html" "b/2020/02/12/\346\211\271\351\207\217\346\215\242\350\241\214\347\254\246\350\275\254\346\215\242CRLF\345\210\260LF/index.html" deleted file mode 100644 index a4cc09b..0000000 --- "a/2020/02/12/\346\211\271\351\207\217\346\215\242\350\241\214\347\254\246\350\275\254\346\215\242CRLF\345\210\260LF/index.html" +++ /dev/null @@ -1,180 +0,0 @@ - - - - - - - -批量转换换行符CRLF到LF - Genge 随笔 - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - -
-
-
-

批量转换换行符CRLF到LF

- -
-
-

关闭Git自动转换功能

git config --global core.autocrlf false
- -

CRLF转换成LF

vscode或者visual studio等一些代码编辑器仅仅支持单个文件的格式转换,以下三种方法均是支持批量转换的方法

-

第一种方法 (亲测))

已经上传到/y-server/doc/format/ 目录下 或者点击下面链接下载最新

-

下载dos2unix工具包

-

dos2unix文档说明

-

结合 find(1) 和 xargs(1) 使用 dos2unix 可以递归地转换目录树中的文本文件。例如,转换当前目录的目录树中所有的 .txt 文件:

-
dos2unix < a.txt
-cat a.txt | dos2unix
-

若文件名中有空格或引号,则需要使用 find(1) 选项 -print0 及相应的 xargs(1) 选项 -0;其他情况下则可以省略它们。也可以结合 -exec 选项来使用 find(1):

-
find . -name '*.txt' -exec dos2unix {} \;
-

在Windows命令提示符中,可以使用下列命令:

-
for /R %G in (*.txt) do dos2unix "%G"
-

PowerShell用户可以在Windows PowerShell中使用如下命令:

-
get-childitem -path . -filter '*.txt' -recurse | foreach-object {dos2unix $_.Fullname}
- -

第二种方法 (没有尝试)

采用EditPlus批量转换文件格式

-

1
2
2

-

第三种方法 (没有尝试)

巧妙的借助git快速批量转换crlf到lf

- -
-
-
-
- -
- - - - - - -
- - - - - - -
- - - -
-
- - \ No newline at end of file diff --git a/2020/07/10/Redis-Guide/index.html b/2020/07/10/Redis-Guide/index.html deleted file mode 100644 index 7ba0a31..0000000 --- a/2020/07/10/Redis-Guide/index.html +++ /dev/null @@ -1,558 +0,0 @@ - - - - - - - -Redis Guide 使用手册 - Genge 随笔 - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - -
-
-
-

Redis Guide 使用手册

- -
-
-

Redis 数据结构与应用

普通字符串

SET 命令

    -
  1. SET KEY VALUE 设置
  2. -
  3. NX 不存在才设置 可用于实现分布式锁
  4. -
  5. XX 存在才设置
  6. -
-

GET 命令

    -
  1. GET KEY 返回 VALUE 值
  2. -
  3. GETSET 返回旧 OLD_VALUE 值 , 并且设置新 NEW_VALUE
  4. -
-

MSET 命令

    -
  1. MSET KEY1 VALUE1 [ KEY2 VALUE2 …] 一次设置多个键值对
  2. -
-

MGET 命令

    -
  1. MGET KEY1 [ KEY2 …] 一次设置多个键值对
    返回VALUE列表, 这在批量获取多个键值对的时候非常实用, 业务层不用做封装
  2. -
-

MSETNX 命令

    -
  1. MSETNX KEY VALUE [ KEY2 VALUE2 …]
    设置多个键值对, 这是一个原子操作, 当且仅当所有KEY都不存在的时候才会成功
  2. -
-

STRLEN 命令

    -
  1. STRLEN KEY 获取值的字节长度
  2. -
-

GETRANGE 命令

    -
  1. GETRANGE KEY start end
    根据指定索引范围设置值,相当于取值的子串
  2. -
-

SETRANGE 命令

    -
  1. SETRANGE KEY startIndex substitute
    根据指定开始索引,开始替换新串substitute; 当startIndex大于值长度(len - 1), redis会自动扩展值长度,并且填充空字节(\x00)
  2. -
-

APPEND 命令

    -
  1. APPEND KEY suffix 对值进行追加操作, 并返回新串长度
    当KEY不存在, 那么APPEND命令等价于SET操作
  2. -
-

INCRBY DECRBY 命令

    -
  1. INCRBY/DECRBY KEY incrment 当存储的值能够被解释为整数时, 可以使用这两个命令来对值进行加或减
    当KEY不存在,那么会被初始化为0,然后再进行操作
  2. -
-

INCR DECR 命令

    -
  1. INCR/DECR KEY incrment 当存储的值能够被解释为整数时, 可以使用这两个命令来对值进行加或减1
    当KEY不存在,那么会被初始化为0,然后再进行操作
  2. -
-

INCRBYFLOAT 命令

    -
  1. INCRBYFLOAT KEY incrment, 命令与INCRBY无差别,主要是针对的是浮点数
    但是没有DECRBYFLOAT命令,可以通过设置incrment为负数来实现减法
    INCRBYFLOAT 命令对值的格式更加宽松,那么值是整数的时候,命令与INCRBY等同,存储的值也会是整数
    注意Redis在处理浮点数的时候,小数位长度是有限制,最长为17位,对于大部分应用是足够的
  2. -
-

散列

结构

- - - - - - - - - - - - - - - - - - - - - - - - - - - -
KEYFIELDVALUE
articletitle“Hello World”
content“son of bitch”
authorgenge
created_at2010-10-10
-

HSET 命令

    -
  1. HSET HASHKEY FIELD VALUE
    如果key或者field不存在, 那么会创建,返回值为1
    如果field存在, 那么会更新,返回值为0
  2. -
-

HSETNX 命令

    -
  1. HSETNX HASH FIELD VALUE
    只在字段不存在的时候设置值,返回值为1,设置失败返回0
  2. -
-

HGET 命令

    -
  1. HGET HASH FIELD VALUE
    获取字段值
  2. -
-

HINCRBY or HINCRFLOATBY

    -
  1. HINCRBY HASH FIELD INCRMENT 对数值进行加减
  2. -
-

HSTRLEN 命令

    -
  1. HSTRLEN HASH FIELD 获取字段值长度
  2. -
-

HEXISTS 命令

    -
  1. 检查字段值是否存在
  2. -
-

HDEL 命令

    -
  1. HDEL HASH FILED 删除字段
  2. -
-

HLEN 命令

    -
  1. HLEN HASH 获取散列表字段个数
  2. -
-

HMSET/HMGET 系列命令

    -
  1. 效果同MSET/MGET
  2. -
-

HKEYS/HVALS/HGETALL 命令

    -
  1. 获取散列表中所有字段FIELD
  2. -
  3. 获取散列表中所有VALUE
  4. -
  5. 获取散列表中所有FIELD和VALUE
    格式为数组: FIELD1:VALUE1:FILED2:VALUE2……
  6. -
-

List列表结构

Redis List列表是一种线性的有序结构

LPUSH 命令

    -
  1. LPUSH KEY VALUE0 [ VALUE1 VALUE2 …] 返回推入会列表元素个数
    向列表左端新增n个VALUE, ‘L’ 表示是left 而不是 list
  2. -
-

RPUSH 命令

    -
  1. RPUSH KEY VALUE0 [ VALUE1 VALUE2 …] 返回推入会列表元素个数
    向列表右端新增n个VALUE, ‘R’ 表示是right
  2. -
-

RPUSHX/LPUSHX 命令

    -
  1. 与上两个命令同,但是在列表KEY不存在的时候的结果不一样,这两个命令在列表KEY不存在的时候会推入失败, 返回0
  2. -
-

LPOP/RPOP

    -
  1. LPOP/RPOP LISTKEY 弹出最左边/右边的元素, 返回POP后的元素
  2. -
-

RPOPLPUSH 命令

    -
  1. RPOPLPUSH source target 返回被弹出的元素值
    将源source列表最右端元素弹出,插入到目标target列表最左端
    其中source和target可以相同,即将列表首尾对调
    当source 为空时,执行会失败,返回空
  2. -
-

LLEN 命令

    -
  1. 获取列表长度
  2. -
-

LINDEX 命令

    -
  1. LINDEX list index 获取列表指定索引元素
    index为正数: 左端为0, 范围为 0 – N-1
    index为负数: 右端为-1, 范围为 -N – -1
  2. -
-

LRANGE 命令

    -
  1. 获取指定索引范围内的所有元素
    LRANGE list start end
    LRANGE list 0 -1 获取全部元素
    当start和end都超出范围时,将会返回空列表;当只有一个超出时,Redis会对超出的索引进行修正,开始索引超出会被修正为0,结束索引会被修正为-1
  2. -
-

LSET 命令

    -
  1. LSET list index new_value
    对列表指定索引元素值更新
  2. -
-

LINSERT 命令

    -
  1. LINSERT list BEFORE/AFTER target_element new_element
  2. -
-

LTRIM 裁剪

    -
  1. LTRIM list start index 删除索引范围外所有元素
  2. -
-

LREM 移除

    -
  1. LREM list count element 返回被移除的元素数量
      -
    • 如果count等于0,那么命令将会移除list中所有值等于element的元素
    • -
    • 如果count等于正数,那么命令会从左端开始扫描,移除列表中值为element的count个元素
    • -
    • 如果count等于负数,那么命令会从右端开始扫描,移除列表中值为element的abs(count)个元素
    • -
    -
  2. -
-

BLPOP 阻塞式左端弹出

    -
  1. BLPOP list1 [ list2 list3 …] timeout
      -
    • 命令会按照传入的列表从左至右挨个检查是否为空,如果发现某个列表不为空,那么执行LPOP操作,返回值为两个元素的数组,第一个元素是被弹出的列表list名,第二个元素是被弹出的元素值;
    • -
    • 如果当前传入的所有list为空,那么Redis将会阻塞等待直至timeout超时,返回空值,超时时间单位为秒,设置为0时表示会一直等待
    • -
    • 如果当前有多个客户端因为某个列表空而阻塞,那么按照先阻塞先服务原则进行唤醒
    • -
    • 这个命令只会当前Redis客户端
    • -
    -
  2. -
-

BRPOP 命令 与上同

BRPOPLPUSH 阻塞式弹出和推入操作 与上同

    -
  1. 可用于实现带有阻塞式的消息队列
  2. -
-

无序集合Set

数据结构

说明无序集合,集合中元素不重复

-

SADD 命令

SADD set element [ element …]

-

返回值为新增元素个数,会去重

-

SREM 命令

SREM set element [element …]
移除一个或多个元素,返回真实移除元素的个数

-

SMOVE 命令

SMOVE source target element 将指定元素从source移除,并且加入到目标集合,当source中不存在element的时候会返回失败

-

SMEMBERS 命令

SMEMBERS set 获取集合所有元素

-

SCARD 命令

    -
  1. SCARD set 获取集合元素个数
  2. -
-

SISMEMBER 命令

    -
  1. SISMEMBER set element 判断指定元素是否存在集合中
  2. -
-

SRANDMEMBER 命令

    -
  1. SRANDMEMBER SET [count] 随机获取集合汇总count个元素,count默认值为1

    -
  2. -
  3. count为正数时候,返回随机不重复的min(count, SCARD) 个元素,属于不放回随机抽取

    -
  4. -
  5. count为负数的时候,随机的机制发生变化,属于放回随机抽取,也就是说返回集合有可能出现重复的元素

    -
  6. -
-

SPOP 命令

    -
  1. SPOP set [count] 随机的重集中移除count个元素,返回被移除的元素集合
  2. -
-

SINTER/SINTERSTORE 命令

    -
  1. SINTER set [set] 求多个集合的交集

    -
  2. -
  3. SINTER dest_set set [set ] 求多个集合的交集,并将结果存储到新的集合中,返回新集合的元素个数

    -
  4. -
-

SUNION/SUNIONSTORE 命令

    -
  1. 并集, 含义同上
  2. -
-

SDIFF/SDIFFSTORE 命令

    -
  1. 差集,含义同上
  2. -
  3. 集合操作都非常消耗性能,可能导致Redis主线程阻塞
  4. -
-

有序集合Sorted SET

对应数据结构

    -
  1. 同时具有有序和集合的性质
  2. -
  3. 每个元素都由一个成员和一个与成员相关联的分值score组成
  4. -
  5. 排行榜最佳实现
  6. -
-

sorted set

- - - - - - - - - - - - - - - - - - - - - - - - - - - -
scoremember
1genge
3apple
7inuby
7oiuby
19qwmok
-

ZADD 命令

    -
  1. ZADD sorted_set [CH] score element [score element] 向有序集合中添加元素
  2. -
  3. 默认返回新添加元素的个数,当命令带 CH 的时候返回修改元素的个数
  4. -
-

ZREM 命令

    -
  1. ZREM sorted_set member [member] 移除指定元素 返回真实被移除的元素个数
  2. -
-

ZSCORE 命令

    -
  1. ZSCORE sorted_set member 获取指定元素的分值
  2. -
-

ZINCRBY 命令

    -
  1. ZINCRBY sorted_set increment_score memeber 对指定元素的分值加减
  2. -
  3. 当member不存在的时候,命令等同于ZADD
  4. -
-

ZCARD 命令

    -
  1. 获取有序集合元素个数
  2. -
-

ZRANK/ZREVRANK 命令

    -
  1. ZRANK sorted_set member 指定元素的的正排名 (从小到大)
  2. -
  3. ZREVRANK sorted_set member 指定元素的的排名 (从大到小)
  4. -
-

ZRANGE / ZREVRANGE 命令

    -
  1. 获取指定范围内成员 ZRANGE/ZREVRANGE sorted_set start end
  2. -
  3. start 和 end均可接受负值, 含义是排名
  4. -
  5. ZRANGE/ZREVRANGE sorted_set start end WITHSCORES 会返回分值和元素值
  6. -
-

ZRANGEBYSCORE/ZREVRANGEBYSCORE 命令

    -
  1. ZRANGEBYSCORE/ZREVRANGEBYSCORE sorted_set min max/max min 获取指定范围分数内的成员
  2. -
  3. WITHSCORES 可以附带返回分数值
  4. -
  5. LIMIT offset count 可以限制返回元素的数量, offset为起止偏移量,count为最大返回数量
  6. -
  7. (min (max 使用左括号的表示开区间,默认是闭区间
  8. -
  9. -inf +inf来表示负无穷和正无穷
  10. -
-

ZCOUNT 命令

    -
  1. ZCOUNT sorted_set min max 获取指定分值范围内的成员数量
  2. -
  3. 和上面的RAGNE命令相同,都支持开闭区间无穷等设置
  4. -
-

ZREMRANGEBYRANK 命令

    -
  1. ZREMRANGEBYRANK sorted_set start end 根据给定的排名区间来移除成员
  2. -
  3. 参数支持负值,表示倒数排名
  4. -
-

ZREMRANGEBYSCORE 命令

    -
  1. ZREMRANGEBYSCORE sorted_set start end 根据给定的分数区间来移除成员
  2. -
  3. 和上面的RAGNE命令相同,都支持开闭区间无穷等设置
  4. -
-

ZINTERSTORE/ZUNIONSTORE 命令

    -
  1. ZINTERSTORE/ZUNIONSTORE destination numbers sorted_set [sorted_set ] 求多个集合的交集和并集
  2. -
  3. numbers为sorted_set 参数的个数
  4. -
  5. 返回交集元素或者并集元素个数
  6. -
  7. 集合元素的分值是由两个集合分值的和
  8. -
  9. [AGGREGATE SUM/MIN/MAX] 可以通过设置聚合函数来控制分值(求和/最小值/最大值)
  10. -
  11. [WEIGHTS w1 w2 w3] 可以为每个集合设置权重,这样计算方式将会是分值乘以权重再相加
  12. -
  13. 除此之外,还可以接受集合(非有序)来执行命令,此时score默认都是1,还可以带WEIGHTS
  14. -
-

ZEANGEBYLEX/ZREVRANGEBYLEX/ZLEXCOUNT/ZREMRANGEBYLEX 系列命令

    -
  1. 以上所有命令格式雷同,都是处理当有序集合中分数全部相当的情况min和max分别指定是字典字母
  2. -
  3. ZEANGEBYLEX sorted_set min max
  4. -
  5. 比如ZEANGEBYLEX sorted_set - +返回所有成员,[a (t 返回字典大于等于a并且小于t的所有成员
  6. -
  7. 逆序/成员个数/移除操作都是类似的
  8. -
-

ZPOPMAX/ZPOPMIN 命令

    -
  1. 弹出分值最高或者最低分值的元素
  2. -
  3. 返回成员和分值
  4. -
  5. ZPOPMAX sorted_set [count]可以通过 count 来指定最多移除的成员数量,默认为1
  6. -
  7. Redis5.0 以上版本才支持
  8. -
-

BZPOPMIN/BZPOPMAX 命令

    -
  1. 阻塞式的最小或最大的弹出操作, 可以接受多个集合参数,进行遍历检测
  2. -
  3. BZPOPMIN/BZPOPMAX sorted_set [sorted_set ] timeout
  4. -
  5. timeout 为 0 表示无限阻塞等待
  6. -
-

HperLogLog

HperLogLog数据结构

    -
  1. 神奇的HyperLogLog算法http://www.rainybowe.com/blog/2017/07/13/%E7%A5%9E%E5%A5%87%E7%9A%84HyperLogLog%E7%AE%97%E6%B3%95/index.html
  2. -
  3. Sketch of the Day: HyperLogLog — Cornerstone of a Big Data Infrastructure http://content.research.neustar.biz/blog/hll.html
  4. -
  5. 这是一个专门解决大数据计数器消耗太多内存问题的一个概率算法,只需要12k就可以统计2^64个元素
  6. -
  7. 当然这不是精确统计,存在误差,数据量大的时候误差有的时候是允许的,可容允的
  8. -
-

PFADD 命令

    -
  1. PFADD hperloglog element [element] 新增元素
  2. -
  3. 当新增元素是的统计基数值发生变化就返回1,否则反正0
  4. -
-

PFCOUNT 命令

    -
  1. PFCOUNT hyperloglog [hyperloglog ...] 计算集合的近视基数
  2. -
  3. 当参数为多个时候,计算方式为:首先求多个集合的并集,然后对并集求近视基数
  4. -
-

PFMERGE 命令

    -
  1. PFMERGE destination hyperloglog [hyperloglog ...]  对多个hyperloglog集合求并集,然后将结果存在dest中

    -
  2. -
  3. PFCOUNT 其实是有调用PFMERGE命令的

    -
  4. -
-

位图

位图结构

    -
  1. Redis位图bitmap是由多个二进制位组成的数组,数组中每一位都有与之对应的偏移量(索引)

    -
  2. -
  3. BITMAP 图

    -
  4. -
- - - - - - - - - - - - - - - - - -
index0123
1001|
-

SETBIT 命令

    -
  1. SETBIT bitmap offset value 设置指定偏移位的值
  2. -
  3. 返回指定偏移量旧值,默认为0
  4. -
  5. bitmap默认按照字节扩展
  6. -
  7. offset只能为正值
  8. -
-

GETBIT 命令

    -
  1. GETBIT bitmap offset获取指定位置的值
  2. -
-

BITCOUNT 命令

    -
  1. BITCOUNT bitmap 统计位图中1的个数
  2. -
  3. BITCOUNT bitmap start end 返回指定字节范围内1的个数,注意start和end为字节偏移量,并不是位offset, 可以使用负数作为参数
  4. -
-

BITPOS 命令

    -
  1. BITPOS bitmap value 查询bitmap中第一个被设置为value值的位置
  2. -
  3. BITPOS bitmap value [start end]  在指定范围内查找,但是返回的offset是基于整个bigmap的偏移
  4. -
  5. start和end可以为负值
  6. -
-

BITOP 命令

    -
  1. BITOP OP result_key bitmap [bitmap ...] 对多个bitmap数组执行op操作,将结果存储在result中
  2. -
  3. op可以是 AND / OR /XOR / NOT
  4. -
-

BITFIELD 命令

    -
  1. BITFIELD bitmap SET type offset value 根据位偏移来设置bitmap中值value,其中type是指定value的类型,比如i8:8位有符号,u16:16位无符号等
  2. -
  3. offset 可以换成 #index, 这样可以以字节位来索引具体位置,然后设置值
  4. -
  5. 可以同时执行多个set命令
  6. -
  7. BITFIELD bitmap GET type offset/#index 获取对应的值,同样的也可以同时执行多个GET
  8. -
  9. BITFIELD bitmap INCRYBY type offset/#index increment 对指定范围值加减操作
  10. -
  11. BITFIELD bitmap [OVERFLOW WRAP/SAT/FAIL] INCRYBY type offset/#index increment 可以用来处理加减法结果溢出的情况,分别为环绕/饱和运算/失败
  12. -
-

BITMAP STRING

    -
  1. 可以把二进制数组当作是字符串来操作
  2. -
  3. GET 命令来获取二进制数组值,返回值为二进制字符串
  4. -
  5. STRLEN 可以得到二进制字符串的长度
  6. -
  7. GETRANGE 获取指定范围的二进制字符串
  8. -
-

GEO位置服务

GEOADD 命令

    -
  1. GEOADD location_set longitude latitude name [longitude latitude name] 添加一个或者多个位置坐标(经纬度)
  2. -
  3. 当执行的是添加的,那么返回添加的位置个数;如果是更新那么返回0
  4. -
-

GEOPOS 命令

    -
  1. GEOPOS location_set name [name ...]  获取指定位置的经纬度
  2. -
  3. 返回值是数组,其中数组元素为二元数组,第一项为经度,第二项为纬度
  4. -
-

GEODIST 命令

    -
  1. GEODIST location_set name1 name2 计算俩个位置的直线距离
  2. -
  3. 默认单位为米,可以通过[unit] 来指定单位m/km/mi英里/ft英寸
  4. -
-

GEORADIUS 命令

    -
  1. GEORADIUS location_set longitude latitude radius unit  获取指定位置为中心点,radius半径内所有的地点
  2. -
  3. WITHDIST 加上这个后缀参数,可以返回地点和地点与中心位置的直线距离
  4. -
  5. WITHCOORD  返回地点和地点坐标
  6. -
  7. [ASD|DESC] 对返回的结果排序
  8. -
  9. [COUNT n] 限制返回地点的数量
  10. -
  11. 可以同时指定多个可选参数
  12. -
-

GEORADIUSBYMEMBER 命令

    -
  1. longitude latitude 参数换成 name地名
  2. -
-

GEOHASH 命令

    -
  1. 获取指定位置的GEOhash值,GEOhash值是经纬度转换而来,并且可以通过GEohash来计算得到经纬度
  2. -
  3. 在上面的两个命令中都可以指定WITHHASH  来返回GEOHASH 而非 经纬度
  4. -
-

GEO数据内部存储结构

    -
  1. 为有序集合,因此可以使用ZSORTED来操作数据,其中score为Geohash值
  2. -
-

Stream流

-
-
-
-
- -
- - - - -
- - - - - - -
- - - -
-
- - \ No newline at end of file diff --git a/_config.yml b/_config.yml new file mode 100644 index 0000000..34077aa --- /dev/null +++ b/_config.yml @@ -0,0 +1,114 @@ +# Hexo Configuration +## Docs: https://hexo.io/docs/configuration.html +## Source: https://github.com/hexojs/hexo/ + +# Site +title: Genge 随笔 +subtitle: "好好学习 实现财富自由" +description: "记录点滴" +keywords: 菜鸡程序员 +author: Genge +language: zh-CN +timezone: "Asia/Shanghai" + +# URL +## If your site is put in a subdirectory, set url as 'http://yoursite.com/child' and root as '/child/' +url: https://blog.genge.cc +root: / +permalink: /:year/:month/:day/:title/ +permalink_defaults: +pretty_urls: + trailing_index: true # Set to false to remove trailing 'index.html' from permalinks + trailing_html: true # Set to false to remove trailing '.html' from permalinks + +# Directory +source_dir: source +public_dir: public +tag_dir: tags +friend_dir: friends +archive_dir: archives +category_dir: categories +code_dir: downloads/code +i18n_dir: :lang +skip_render: +rss: /atom.xml + +# Writing +new_post_name: :title.md # File name of new posts +default_layout: post +titlecase: false # Transform title into titlecase +external_link: + enable: true # Open external links in new tab + field: site # Apply to the whole site + exclude: "" +filename_case: 0 +render_drafts: false +post_asset_folder: false +relative_link: false +future: true +highlight: + enable: true + auto_detect: true + line_number: false # This value must be `false` + wrap: false # This value must be `false` + tab_replace: " " # 4 spaces + hljs: true # This value must be `true` + +#RSS订阅 +plugin: + - hexo-generator-feed +#Feed Atom +feed: +type: atom +path: atom.xml +limit: 20 + +# Home page setting +# path: Root path for your blogs index page. (default = '') +# per_page: Posts displayed per page. (0 = disable pagination) +# order_by: Posts order. (Order by date descending by default) +index_generator: + path: "" + per_page: 10 + order_by: -date + +# Category & Tag +default_category: uncategorized +category_map: +tag_map: + +# Metadata elements +## https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta +meta_generator: true + +# Date / Time format +## Hexo uses Moment.js to parse and display date +## You can customize the date format as defined in +## http://momentjs.com/docs/#/displaying/format/ +date_format: YYYY-MM-DD +time_format: HH:mm:ss +## Use post's date for updated date unless set in front-matter +updated_option: "date" + +# Pagination +## Set per_page to 0 to disable pagination +per_page: 10 +pagination_dir: page + +# Include / Exclude file(s) +## include:/exclude: options only apply to the 'source/' folder +include: +exclude: +ignore: + +# Extensions +## Plugins: https://hexo.io/plugins/ +## Themes: https://hexo.io/themes/ +theme: pure + +# Deployment +## Docs: https://hexo.io/docs/deployment.html +deploy: + type: git + repository: git@github.com:hzgcoding/hzgcoding.github.io.git + branch: master diff --git a/_config_themes.yml b/_config_themes.yml new file mode 100644 index 0000000..1a88f6a --- /dev/null +++ b/_config_themes.yml @@ -0,0 +1,57 @@ +favicon: data:image/png;base64,%0A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 +avatar: data:image/png;base64,%0A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 +katex: false +force_https: true +check_update: true + +menus: + - name: 首页 + link: / + - name: 归档 + link: /archives/ + - name: 标签 + link: /tags/ + - name: 时间线 + link: /timeline/ + - name: 关于 + link: /about/ + - name: 友链 + link: /friends/ + - name: rss + link: /atom.xml + +post: + feature: true +disqus: + enable: false + shortname: "Your shortname here" + api: "Your api here" + apikey: "Your api key here" + admin: "Your disqus username here" +gitalk: + enable: true + clientId: "387beb7791af325600a4" + clientSecret: "b2b890ffae6bd3526351c3ed79ba4f8ab9cbd4f6" + repository: "git@github.com:hzgcoding/BlogComment.git" + owner: "hzgcoding" +valine: + enable: false + appId: "Your appID here" + appKey: "Your appKey here" + placeholder: "Your placeholder here" + pageSize: 10 +web_analytics: + enable: false + google: UA-12345678-1 + baidu: 12345678 +footer: | + Footer HTML +friends: + - name: 777 + link: http://younfor.com/ + logo: https://younfor.com/images/avatar.png + description: "字节跳动大神,算法大佬" + - name: hl174 + link: http://hl174.com/ + logo: https://younfor.com/images/avatar.png + description: "深圳腾讯大神,算法大佬" diff --git a/about/index.html b/about/index.html deleted file mode 100644 index b5761ff..0000000 --- a/about/index.html +++ /dev/null @@ -1,138 +0,0 @@ - - - - - - - -关于 - Genge 随笔 - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - -
-
-
-

关于

- -
-
-

Hello World, 字节跳动搬砖五年多!

- -
-
-
-
- - - - - - -
- - - -
-
- - \ No newline at end of file diff --git a/archives/2020/01/index.html b/archives/2020/01/index.html deleted file mode 100644 index 3686fac..0000000 --- a/archives/2020/01/index.html +++ /dev/null @@ -1,243 +0,0 @@ - - - - - - - -归档 - Genge 随笔 - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - - - -
- - - 下一页 - -
- - - -
-
- - \ No newline at end of file diff --git a/archives/2020/01/page/2/index.html b/archives/2020/01/page/2/index.html deleted file mode 100644 index d2d5757..0000000 --- a/archives/2020/01/page/2/index.html +++ /dev/null @@ -1,144 +0,0 @@ - - - - - - - -归档 - Genge 随笔 - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - -
-
- - -

2020

- -

1 月

- - - -
- -
- 2020-01-22 -
linux 内核RingBuffer实现
-
-
-
- - - -
-
- -
- - 上一页 - - -
- - - -
-
- - \ No newline at end of file diff --git a/archives/2020/02/index.html b/archives/2020/02/index.html deleted file mode 100644 index e4f65f5..0000000 --- a/archives/2020/02/index.html +++ /dev/null @@ -1,153 +0,0 @@ - - - - - - - -归档 - Genge 随笔 - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - -
-
- - -

2020

- -

2 月

- - - -
- -
- 2020-02-12 -
批量转换换行符CRLF到LF
-
-
-
- - - -
- -
- 2020-02-05 -
Flag
-
-
-
- - - -
-
- -
- - -
- - - -
-
- - \ No newline at end of file diff --git a/archives/2020/07/index.html b/archives/2020/07/index.html deleted file mode 100644 index cb88b64..0000000 --- a/archives/2020/07/index.html +++ /dev/null @@ -1,142 +0,0 @@ - - - - - - - -归档 - Genge 随笔 - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - -
-
- - -

2020

- -

7 月

- - - -
- -
- 2020-07-10 -
Redis Guide 使用手册
-
-
-
- - - -
-
- -
- - -
- - - -
-
- - \ No newline at end of file diff --git a/archives/2020/index.html b/archives/2020/index.html deleted file mode 100644 index a2a1983..0000000 --- a/archives/2020/index.html +++ /dev/null @@ -1,241 +0,0 @@ - - - - - - - -归档 - Genge 随笔 - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - - - -
- - - 下一页 - -
- - - -
-
- - \ No newline at end of file diff --git a/archives/2020/page/2/index.html b/archives/2020/page/2/index.html deleted file mode 100644 index 5ad56c1..0000000 --- a/archives/2020/page/2/index.html +++ /dev/null @@ -1,175 +0,0 @@ - - - - - - - -归档 - Genge 随笔 - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - - - -
- - 上一页 - - -
- - - -
-
- - \ No newline at end of file diff --git a/archives/index.html b/archives/index.html deleted file mode 100644 index 31c00cd..0000000 --- a/archives/index.html +++ /dev/null @@ -1,241 +0,0 @@ - - - - - - - -归档 - Genge 随笔 - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - - - -
- - - 下一页 - -
- - - -
-
- - \ No newline at end of file diff --git a/archives/page/2/index.html b/archives/page/2/index.html deleted file mode 100644 index d587732..0000000 --- a/archives/page/2/index.html +++ /dev/null @@ -1,175 +0,0 @@ - - - - - - - -归档 - Genge 随笔 - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - - - -
- - 上一页 - - -
- - - -
-
- - \ No newline at end of file diff --git a/atom.xml b/atom.xml deleted file mode 100644 index 78ea911..0000000 --- a/atom.xml +++ /dev/null @@ -1,371 +0,0 @@ - - - Genge 随笔 - - 好好学习 实现财富自由 - - - - 2020-07-10T10:28:44.000Z - https://blog.genge.cc/ - - - Genge - - - - Hexo - - - Redis Guide 使用手册 - - https://blog.genge.cc/2020/07/10/Redis-Guide/ - 2020-07-10T10:28:44.000Z - 2020-07-10T10:28:44.000Z - - Redis 数据结构与应用

普通字符串

SET 命令

  1. SET KEY VALUE 设置
  2. NX 不存在才设置 可用于实现分布式锁
  3. XX 存在才设置

GET 命令

  1. GET KEY 返回 VALUE 值
  2. GETSET 返回旧 OLD_VALUE 值 , 并且设置新 NEW_VALUE

MSET 命令

  1. MSET KEY1 VALUE1 [ KEY2 VALUE2 …] 一次设置多个键值对

MGET 命令

  1. MGET KEY1 [ KEY2 …] 一次设置多个键值对
    返回VALUE列表, 这在批量获取多个键值对的时候非常实用, 业务层不用做封装

MSETNX 命令

  1. MSETNX KEY VALUE [ KEY2 VALUE2 …]
    设置多个键值对, 这是一个原子操作, 当且仅当所有KEY都不存在的时候才会成功

STRLEN 命令

  1. STRLEN KEY 获取值的字节长度

GETRANGE 命令

  1. GETRANGE KEY start end
    根据指定索引范围设置值,相当于取值的子串

SETRANGE 命令

  1. SETRANGE KEY startIndex substitute
    根据指定开始索引,开始替换新串substitute; 当startIndex大于值长度(len - 1), redis会自动扩展值长度,并且填充空字节(\x00)

APPEND 命令

  1. APPEND KEY suffix 对值进行追加操作, 并返回新串长度
    当KEY不存在, 那么APPEND命令等价于SET操作

INCRBY DECRBY 命令

  1. INCRBY/DECRBY KEY incrment 当存储的值能够被解释为整数时, 可以使用这两个命令来对值进行加或减
    当KEY不存在,那么会被初始化为0,然后再进行操作

INCR DECR 命令

  1. INCR/DECR KEY incrment 当存储的值能够被解释为整数时, 可以使用这两个命令来对值进行加或减1
    当KEY不存在,那么会被初始化为0,然后再进行操作

INCRBYFLOAT 命令

  1. INCRBYFLOAT KEY incrment, 命令与INCRBY无差别,主要是针对的是浮点数
    但是没有DECRBYFLOAT命令,可以通过设置incrment为负数来实现减法
    INCRBYFLOAT 命令对值的格式更加宽松,那么值是整数的时候,命令与INCRBY等同,存储的值也会是整数
    注意Redis在处理浮点数的时候,小数位长度是有限制,最长为17位,对于大部分应用是足够的

散列

结构

KEYFIELDVALUE
articletitle“Hello World”
content“son of bitch”
authorgenge
created_at2010-10-10

HSET 命令

  1. HSET HASHKEY FIELD VALUE
    如果key或者field不存在, 那么会创建,返回值为1
    如果field存在, 那么会更新,返回值为0

HSETNX 命令

  1. HSETNX HASH FIELD VALUE
    只在字段不存在的时候设置值,返回值为1,设置失败返回0

HGET 命令

  1. HGET HASH FIELD VALUE
    获取字段值

HINCRBY or HINCRFLOATBY

  1. HINCRBY HASH FIELD INCRMENT 对数值进行加减

HSTRLEN 命令

  1. HSTRLEN HASH FIELD 获取字段值长度

HEXISTS 命令

  1. 检查字段值是否存在

HDEL 命令

  1. HDEL HASH FILED 删除字段

HLEN 命令

  1. HLEN HASH 获取散列表字段个数

HMSET/HMGET 系列命令

  1. 效果同MSET/MGET

HKEYS/HVALS/HGETALL 命令

  1. 获取散列表中所有字段FIELD
  2. 获取散列表中所有VALUE
  3. 获取散列表中所有FIELD和VALUE
    格式为数组: FIELD1:VALUE1:FILED2:VALUE2……

List列表结构

Redis List列表是一种线性的有序结构

LPUSH 命令

  1. LPUSH KEY VALUE0 [ VALUE1 VALUE2 …] 返回推入会列表元素个数
    向列表左端新增n个VALUE, ‘L’ 表示是left 而不是 list

RPUSH 命令

  1. RPUSH KEY VALUE0 [ VALUE1 VALUE2 …] 返回推入会列表元素个数
    向列表右端新增n个VALUE, ‘R’ 表示是right

RPUSHX/LPUSHX 命令

  1. 与上两个命令同,但是在列表KEY不存在的时候的结果不一样,这两个命令在列表KEY不存在的时候会推入失败, 返回0

LPOP/RPOP

  1. LPOP/RPOP LISTKEY 弹出最左边/右边的元素, 返回POP后的元素

RPOPLPUSH 命令

  1. RPOPLPUSH source target 返回被弹出的元素值
    将源source列表最右端元素弹出,插入到目标target列表最左端
    其中source和target可以相同,即将列表首尾对调
    当source 为空时,执行会失败,返回空

LLEN 命令

  1. 获取列表长度

LINDEX 命令

  1. LINDEX list index 获取列表指定索引元素
    index为正数: 左端为0, 范围为 0 – N-1
    index为负数: 右端为-1, 范围为 -N – -1

LRANGE 命令

  1. 获取指定索引范围内的所有元素
    LRANGE list start end
    LRANGE list 0 -1 获取全部元素
    当start和end都超出范围时,将会返回空列表;当只有一个超出时,Redis会对超出的索引进行修正,开始索引超出会被修正为0,结束索引会被修正为-1

LSET 命令

  1. LSET list index new_value
    对列表指定索引元素值更新

LINSERT 命令

  1. LINSERT list BEFORE/AFTER target_element new_element

LTRIM 裁剪

  1. LTRIM list start index 删除索引范围外所有元素

LREM 移除

  1. LREM list count element 返回被移除的元素数量
    • 如果count等于0,那么命令将会移除list中所有值等于element的元素
    • 如果count等于正数,那么命令会从左端开始扫描,移除列表中值为element的count个元素
    • 如果count等于负数,那么命令会从右端开始扫描,移除列表中值为element的abs(count)个元素

BLPOP 阻塞式左端弹出

  1. BLPOP list1 [ list2 list3 …] timeout
    • 命令会按照传入的列表从左至右挨个检查是否为空,如果发现某个列表不为空,那么执行LPOP操作,返回值为两个元素的数组,第一个元素是被弹出的列表list名,第二个元素是被弹出的元素值;
    • 如果当前传入的所有list为空,那么Redis将会阻塞等待直至timeout超时,返回空值,超时时间单位为秒,设置为0时表示会一直等待
    • 如果当前有多个客户端因为某个列表空而阻塞,那么按照先阻塞先服务原则进行唤醒
    • 这个命令只会当前Redis客户端

BRPOP 命令 与上同

BRPOPLPUSH 阻塞式弹出和推入操作 与上同

  1. 可用于实现带有阻塞式的消息队列

无序集合Set

数据结构

说明无序集合,集合中元素不重复

SADD 命令

SADD set element [ element …]

返回值为新增元素个数,会去重

SREM 命令

SREM set element [element …]
移除一个或多个元素,返回真实移除元素的个数

SMOVE 命令

SMOVE source target element 将指定元素从source移除,并且加入到目标集合,当source中不存在element的时候会返回失败

SMEMBERS 命令

SMEMBERS set 获取集合所有元素

SCARD 命令

  1. SCARD set 获取集合元素个数

SISMEMBER 命令

  1. SISMEMBER set element 判断指定元素是否存在集合中

SRANDMEMBER 命令

  1. SRANDMEMBER SET [count] 随机获取集合汇总count个元素,count默认值为1

  2. count为正数时候,返回随机不重复的min(count, SCARD) 个元素,属于不放回随机抽取

  3. count为负数的时候,随机的机制发生变化,属于放回随机抽取,也就是说返回集合有可能出现重复的元素

SPOP 命令

  1. SPOP set [count] 随机的重集中移除count个元素,返回被移除的元素集合

SINTER/SINTERSTORE 命令

  1. SINTER set [set] 求多个集合的交集

  2. SINTER dest_set set [set ] 求多个集合的交集,并将结果存储到新的集合中,返回新集合的元素个数

SUNION/SUNIONSTORE 命令

  1. 并集, 含义同上

SDIFF/SDIFFSTORE 命令

  1. 差集,含义同上
  2. 集合操作都非常消耗性能,可能导致Redis主线程阻塞

有序集合Sorted SET

对应数据结构

  1. 同时具有有序和集合的性质
  2. 每个元素都由一个成员和一个与成员相关联的分值score组成
  3. 排行榜最佳实现

sorted set

scoremember
1genge
3apple
7inuby
7oiuby
19qwmok

ZADD 命令

  1. ZADD sorted_set [CH] score element [score element] 向有序集合中添加元素
  2. 默认返回新添加元素的个数,当命令带 CH 的时候返回修改元素的个数

ZREM 命令

  1. ZREM sorted_set member [member] 移除指定元素 返回真实被移除的元素个数

ZSCORE 命令

  1. ZSCORE sorted_set member 获取指定元素的分值

ZINCRBY 命令

  1. ZINCRBY sorted_set increment_score memeber 对指定元素的分值加减
  2. 当member不存在的时候,命令等同于ZADD

ZCARD 命令

  1. 获取有序集合元素个数

ZRANK/ZREVRANK 命令

  1. ZRANK sorted_set member 指定元素的的正排名 (从小到大)
  2. ZREVRANK sorted_set member 指定元素的的排名 (从大到小)

ZRANGE / ZREVRANGE 命令

  1. 获取指定范围内成员 ZRANGE/ZREVRANGE sorted_set start end
  2. start 和 end均可接受负值, 含义是排名
  3. ZRANGE/ZREVRANGE sorted_set start end WITHSCORES 会返回分值和元素值

ZRANGEBYSCORE/ZREVRANGEBYSCORE 命令

  1. ZRANGEBYSCORE/ZREVRANGEBYSCORE sorted_set min max/max min 获取指定范围分数内的成员
  2. WITHSCORES 可以附带返回分数值
  3. LIMIT offset count 可以限制返回元素的数量, offset为起止偏移量,count为最大返回数量
  4. (min (max 使用左括号的表示开区间,默认是闭区间
  5. -inf +inf来表示负无穷和正无穷

ZCOUNT 命令

  1. ZCOUNT sorted_set min max 获取指定分值范围内的成员数量
  2. 和上面的RAGNE命令相同,都支持开闭区间无穷等设置

ZREMRANGEBYRANK 命令

  1. ZREMRANGEBYRANK sorted_set start end 根据给定的排名区间来移除成员
  2. 参数支持负值,表示倒数排名

ZREMRANGEBYSCORE 命令

  1. ZREMRANGEBYSCORE sorted_set start end 根据给定的分数区间来移除成员
  2. 和上面的RAGNE命令相同,都支持开闭区间无穷等设置

ZINTERSTORE/ZUNIONSTORE 命令

  1. ZINTERSTORE/ZUNIONSTORE destination numbers sorted_set [sorted_set ] 求多个集合的交集和并集
  2. numbers为sorted_set 参数的个数
  3. 返回交集元素或者并集元素个数
  4. 集合元素的分值是由两个集合分值的和
  5. [AGGREGATE SUM/MIN/MAX] 可以通过设置聚合函数来控制分值(求和/最小值/最大值)
  6. [WEIGHTS w1 w2 w3] 可以为每个集合设置权重,这样计算方式将会是分值乘以权重再相加
  7. 除此之外,还可以接受集合(非有序)来执行命令,此时score默认都是1,还可以带WEIGHTS

ZEANGEBYLEX/ZREVRANGEBYLEX/ZLEXCOUNT/ZREMRANGEBYLEX 系列命令

  1. 以上所有命令格式雷同,都是处理当有序集合中分数全部相当的情况min和max分别指定是字典字母
  2. ZEANGEBYLEX sorted_set min max
  3. 比如ZEANGEBYLEX sorted_set - +返回所有成员,[a (t 返回字典大于等于a并且小于t的所有成员
  4. 逆序/成员个数/移除操作都是类似的

ZPOPMAX/ZPOPMIN 命令

  1. 弹出分值最高或者最低分值的元素
  2. 返回成员和分值
  3. ZPOPMAX sorted_set [count]可以通过 count 来指定最多移除的成员数量,默认为1
  4. Redis5.0 以上版本才支持

BZPOPMIN/BZPOPMAX 命令

  1. 阻塞式的最小或最大的弹出操作, 可以接受多个集合参数,进行遍历检测
  2. BZPOPMIN/BZPOPMAX sorted_set [sorted_set ] timeout
  3. timeout 为 0 表示无限阻塞等待

HperLogLog

HperLogLog数据结构

  1. 神奇的HyperLogLog算法http://www.rainybowe.com/blog/2017/07/13/%E7%A5%9E%E5%A5%87%E7%9A%84HyperLogLog%E7%AE%97%E6%B3%95/index.html
  2. Sketch of the Day: HyperLogLog — Cornerstone of a Big Data Infrastructure http://content.research.neustar.biz/blog/hll.html
  3. 这是一个专门解决大数据计数器消耗太多内存问题的一个概率算法,只需要12k就可以统计2^64个元素
  4. 当然这不是精确统计,存在误差,数据量大的时候误差有的时候是允许的,可容允的

PFADD 命令

  1. PFADD hperloglog element [element] 新增元素
  2. 当新增元素是的统计基数值发生变化就返回1,否则反正0

PFCOUNT 命令

  1. PFCOUNT hyperloglog [hyperloglog ...] 计算集合的近视基数
  2. 当参数为多个时候,计算方式为:首先求多个集合的并集,然后对并集求近视基数

PFMERGE 命令

  1. PFMERGE destination hyperloglog [hyperloglog ...]  对多个hyperloglog集合求并集,然后将结果存在dest中

  2. PFCOUNT 其实是有调用PFMERGE命令的

位图

位图结构

  1. Redis位图bitmap是由多个二进制位组成的数组,数组中每一位都有与之对应的偏移量(索引)

  2. BITMAP 图

index0123
1001|

SETBIT 命令

  1. SETBIT bitmap offset value 设置指定偏移位的值
  2. 返回指定偏移量旧值,默认为0
  3. bitmap默认按照字节扩展
  4. offset只能为正值

GETBIT 命令

  1. GETBIT bitmap offset获取指定位置的值

BITCOUNT 命令

  1. BITCOUNT bitmap 统计位图中1的个数
  2. BITCOUNT bitmap start end 返回指定字节范围内1的个数,注意start和end为字节偏移量,并不是位offset, 可以使用负数作为参数

BITPOS 命令

  1. BITPOS bitmap value 查询bitmap中第一个被设置为value值的位置
  2. BITPOS bitmap value [start end]  在指定范围内查找,但是返回的offset是基于整个bigmap的偏移
  3. start和end可以为负值

BITOP 命令

  1. BITOP OP result_key bitmap [bitmap ...] 对多个bitmap数组执行op操作,将结果存储在result中
  2. op可以是 AND / OR /XOR / NOT

BITFIELD 命令

  1. BITFIELD bitmap SET type offset value 根据位偏移来设置bitmap中值value,其中type是指定value的类型,比如i8:8位有符号,u16:16位无符号等
  2. offset 可以换成 #index, 这样可以以字节位来索引具体位置,然后设置值
  3. 可以同时执行多个set命令
  4. BITFIELD bitmap GET type offset/#index 获取对应的值,同样的也可以同时执行多个GET
  5. BITFIELD bitmap INCRYBY type offset/#index increment 对指定范围值加减操作
  6. BITFIELD bitmap [OVERFLOW WRAP/SAT/FAIL] INCRYBY type offset/#index increment 可以用来处理加减法结果溢出的情况,分别为环绕/饱和运算/失败

BITMAP STRING

  1. 可以把二进制数组当作是字符串来操作
  2. GET 命令来获取二进制数组值,返回值为二进制字符串
  3. STRLEN 可以得到二进制字符串的长度
  4. GETRANGE 获取指定范围的二进制字符串

GEO位置服务

GEOADD 命令

  1. GEOADD location_set longitude latitude name [longitude latitude name] 添加一个或者多个位置坐标(经纬度)
  2. 当执行的是添加的,那么返回添加的位置个数;如果是更新那么返回0

GEOPOS 命令

  1. GEOPOS location_set name [name ...]  获取指定位置的经纬度
  2. 返回值是数组,其中数组元素为二元数组,第一项为经度,第二项为纬度

GEODIST 命令

  1. GEODIST location_set name1 name2 计算俩个位置的直线距离
  2. 默认单位为米,可以通过[unit] 来指定单位m/km/mi英里/ft英寸

GEORADIUS 命令

  1. GEORADIUS location_set longitude latitude radius unit  获取指定位置为中心点,radius半径内所有的地点
  2. WITHDIST 加上这个后缀参数,可以返回地点和地点与中心位置的直线距离
  3. WITHCOORD  返回地点和地点坐标
  4. [ASD|DESC] 对返回的结果排序
  5. [COUNT n] 限制返回地点的数量
  6. 可以同时指定多个可选参数

GEORADIUSBYMEMBER 命令

  1. longitude latitude 参数换成 name地名

GEOHASH 命令

  1. 获取指定位置的GEOhash值,GEOhash值是经纬度转换而来,并且可以通过GEohash来计算得到经纬度
  2. 在上面的两个命令中都可以指定WITHHASH  来返回GEOHASH 而非 经纬度

GEO数据内部存储结构

  1. 为有序集合,因此可以使用ZSORTED来操作数据,其中score为Geohash值

Stream流

]]>
- - - - - <h1 id="Redis-数据结构与应用"><a href="#Redis-数据结构与应用" class="headerlink" title="Redis 数据结构与应用"></a>Redis 数据结构与应用</h1><h2 id="普通字符串"><a href="#普通字符 - - - - - - - - - -
- - - 批量转换换行符CRLF到LF - - https://blog.genge.cc/2020/02/12/%E6%89%B9%E9%87%8F%E6%8D%A2%E8%A1%8C%E7%AC%A6%E8%BD%AC%E6%8D%A2CRLF%E5%88%B0LF/ - 2020-02-12T05:25:41.000Z - 2020-02-12T05:25:41.000Z - - 关闭Git自动转换功能
git config --global core.autocrlf false

CRLF转换成LF

vscode或者visual studio等一些代码编辑器仅仅支持单个文件的格式转换,以下三种方法均是支持批量转换的方法

第一种方法 (亲测))

已经上传到/y-server/doc/format/ 目录下 或者点击下面链接下载最新

下载dos2unix工具包

dos2unix文档说明

结合 find(1) 和 xargs(1) 使用 dos2unix 可以递归地转换目录树中的文本文件。例如,转换当前目录的目录树中所有的 .txt 文件:

dos2unix < a.txtcat a.txt | dos2unix

若文件名中有空格或引号,则需要使用 find(1) 选项 -print0 及相应的 xargs(1) 选项 -0;其他情况下则可以省略它们。也可以结合 -exec 选项来使用 find(1):

find . -name '*.txt' -exec dos2unix {} \;

在Windows命令提示符中,可以使用下列命令:

for /R %G in (*.txt) do dos2unix "%G"

PowerShell用户可以在Windows PowerShell中使用如下命令:

get-childitem -path . -filter '*.txt' -recurse | foreach-object {dos2unix $_.Fullname}

第二种方法 (没有尝试)

采用EditPlus批量转换文件格式

1
2
2

第三种方法 (没有尝试)

巧妙的借助git快速批量转换crlf到lf

]]>
- - - - - <h2 id="关闭Git自动转换功能"><a href="#关闭Git自动转换功能" class="headerlink" title="关闭Git自动转换功能"></a>关闭Git自动转换功能</h2><pre><code class="hljs angelscript">g - - - - - - - -
- - - Flag - - https://blog.genge.cc/2020/02/05/Flag/ - 2020-02-05T06:54:05.000Z - 2020-02-05T06:54:05.000Z - - 重要的事情说三遍
  • 做完功能要自测!!!
  • 做完功能要自测!!!
  • 做完功能要自测!!!
]]>
- - - - - <h2 id="重要的事情说三遍"><a href="#重要的事情说三遍" class="headerlink" title="重要的事情说三遍"></a><strong>重要的事情说三遍</strong></h2><ul> -<li>做完功能要自测!!!</li> -<li>做完功 - - - - - - - - - -
- - - Linux shell语言——dash和bash - - https://blog.genge.cc/2020/01/23/Linux%20shell%E8%AF%AD%E8%A8%80%E2%80%94%E2%80%94dash%E5%92%8Cbash/ - 2020-01-23T07:13:45.000Z - 2020-01-23T07:13:45.000Z - - 什么是bash ?

Bash(GNU Bourne-Again Shell)是许多Linux平台的内定Shell,事实上,还有许多传统UNIX上用的Shell,像tcsh、csh、ash、bsh、ksh等等。

GNU/Linux 操作系统中的 /bin/sh 本是 bash (Bourne-Again Shell) 的符号链接,但鉴于 bash 过于复杂,有人把 bash 从 NetBSD 移植到 Linux 并更名为 dash (Debian Almquist Shell),并建议将 /bin/sh 指向它,以获得更快的脚本执行速度。Dash Shell 比 Bash Shell 小的多,符合POSIX标准。

Debian和Ubuntu中,/bin/sh默认已经指向dash,这是一个不同于bash的shell,它主要是为了执行脚本而出现,而不是交互,它速度更快,但功能相比bash要少很多,语法严格遵守POSIX标准。

就是这个倒霉的dash解释器使得我按照bash语法写的shell 脚本不能运行。

要知道自己的/bin/sh指向何种解释器,可以用 ls /bin/sh -al 命令查看:

$ ls /bin/sh -allrwxrwxrwx 1 root root 4 1116 15:33 /bin/sh -> bash

以上结果就表示当前系统用的是dash解释器。

切换到bash的方式其实挺简单的,关键是一直没找出这个原因……

修改默认的sh,可以采用命令sudo dpkg-reconfigure dash

会出现一个图片状的配置菜单,选no就可以了

再次检查一下, ls /bin/sh -al 发现软链接指向/bin/bash

         lrwxrwxrwx 1 root root 4 11月 16 15:33 /bin/sh -> bash

注:dash 和 bash 语法上的主要的区别有:

  1. 定义函数

bash: function在bash中为关键字

dash: dash中没有function这个关键字

  1. select var in list; do command; done

bash:支持

dash:不支持, 替代方法:采用while+read+case来实现

  1. echo {0..10}

bash:支持{n..m}展开

dash:不支持,替代方法, 采用seq外部命令

  1. here string

bash:支持here string

dash:不支持, 替代方法:可采用here documents

  1. &word重定向标准输出和标准错误

bash: 当word为非数字时,>&word变成重定向标准错误和标准输出到文件word

dash: >&word, word不支持非数字, 替代方法: >word 2>&1; 常见用法 >/dev/null 2>&1

  1. 数组

bash: 支持数组, bash4支持关联数组

dash: 不支持数组,替代方法, 采用变量名+序号来实现类似的效果

  1. 子字符串扩展

bash: 支持${parameter:offset:length},${parameter:offset}

dash: 不支持, 替代方法:采用expr或cut外部命令代替

  1. 大小写转换

bash: 支持${parameter^pattern},${parameter^^pattern},${parameter,pattern},${parameter,,pattern}

dash: 不支持,替代方法:采用tr/sed/awk等外部命令转换

  1. 进程替换<(command), >(command)

bash: 支持进程替换

dash: 不支持, 替代方法, 通过临时文件中转

  1. [ string1 = string2 ] 和 [ string1 == string2 ]

bash: 支持两者

dash: 只支持=

  1. [[ 加强版test

bash: 支持[[ ]], 可实现正则匹配等强大功能

dash: 不支持[[ ]], 替代方法,采用外部命令

  1. for (( expr1 ; expr2 ; expr3 )) ; do list ; done

bash: 支持C语言格式的for循环

dash: 不支持该格式的for, 替代方法,用while+$((expression))实现

  1. let命令和((expression))

bash: 有内置命令let, 也支持((expression))方式

dash: 不支持,替代方法,采用$((expression))或者外部命令做计算

  1. $((expression))

bash: 支持id++,id–,++id,–id这样到表达式

dash: 不支持++,–, 替代方法:id+=1,id-=1, id=id+1,id=id-1

]]>
- - - - - <p>什么是bash ?</p> -<p>Bash(GNU Bourne-Again Shell)是许多Linux平台的内定Shell,事实上,还有许多传统UNIX上用的Shell,像tcsh、csh、ash、bsh、ksh等等。</p> -<p>GNU&#x2F;Linux 操作系 - - - - - - - - - -
- - - 单生产者单消费者环形缓冲 - - https://blog.genge.cc/2020/01/23/%E5%8D%95%E7%94%9F%E4%BA%A7%E8%80%85%E5%8D%95%E6%B6%88%E8%B4%B9%E8%80%85%E7%8E%AF%E5%BD%A2%E7%BC%93%E5%86%B2/ - 2020-01-23T07:12:50.000Z - 2020-01-23T07:12:50.000Z - - 头文件

#ifndef _SIMPLE_LOOP_BUFFER_H__#define _SIMPLE_LOOP_BUFFER_H__#include "gntype.h"#include "gnmutex.h"#include "gnlock.h"#include "server_define.h"using namespace Storm;typedef struct _tagBlock{    _tagBlock()     {        memset(pBuffer, 0, sizeof(char)*(MAX_CACHE_PACKET_SIZE + 1));    }    ~_tagBlock() {};    char  pBuffer[MAX_CACHE_PACKET_SIZE + 1];     char* GetBuff() { return pBuffer; }    int32 GetBuffLen() { return MAX_CACHE_PACKET_SIZE + 1; }}CBlock;class CSimpleLoopBufferEx{public:    CSimpleLoopBufferEx();    ~CSimpleLoopBufferEx();    void Clear();    /**    * @brief    * 初始化循环缓冲区    * @param nSize : 初始化虚幻缓冲区的大小,实际大小为nSize+1    * @return 成功返回true,失败返回false    */    bool Init();    /**    * @brief    * 将需要存储的Buffer拷贝到循环缓冲区的结尾    * @param pData : [输入参数]指向需要插入循环缓冲区的Buffer起始位置    * @param nLen : 指向需要插入的Buffer的长度    * @return 如果循环缓冲区拥有的大小大于等于nLen,返回true,否则返回false    * @remark 此函数不是线程安全的    */    INT32 PushBack(const CHAR *pData, INT32 nLen);    /**    * @brief    * 从循环缓冲区的起始位置取nLen长度的Buffer,拷贝放入Buffer中    * @param pBuf : [输入输出参数]获取数据的Buffer的起始指针    * @param nLen : 需要读出的Buffer长度    * @return 如果有足够所需读出的数据,返回true,否则返回false    * @remark 此函数不是线程安全的    */    INT32 PopFront(CHAR * &pBuf, INT32 nLen,CHAR* szData);    /**    * @brief    * 丢弃nLen长度的数据    * @param nLen : 需要丢弃的长度    * @return void    * @remark 此函数不是线程安全的    */    bool DiscardFront(INT32 nLen);    /**    * @brief    * 获取剩余可用空间大小    * @return INT32    * @remark 此函数不是线程安全的    */    INT32 GetFreeSpare();    /**    * @brief    * 拷贝内存中的数据    * @param nReadOffSet : m_pNextRead的偏移量    * @param nLen : 需要读出的Buffer长度    * @remark 此函数不是线程安全的    */    INT32 GetData(INT32 nReadOffSet,INT32 nLen, CHAR* szData);private:    CHAR    *m_pBuffer;    CHAR    *m_pNextRead;    CHAR    *m_pNextWrite;    CHAR    *m_pEnd;};#endif

实现

#include "simpleloopbuffer.h"#include <stdio.h>#include <memory.h>#include "gndebug.h"#include "../../common/commonloggerex.h"#include "gate_factory.h"using namespace Storm;CSimpleLoopBufferEx::CSimpleLoopBufferEx() :    m_pBuffer(NULL),    m_pNextRead(NULL),    m_pNextWrite(NULL),    m_pEnd(NULL){}CSimpleLoopBufferEx::~CSimpleLoopBufferEx(){    if (NULL != m_pBuffer)    {        CGateFactory::Instance()->ReleaseBlock((CBlock*)m_pBuffer);        m_pBuffer = NULL;        //delete[] m_pBuffer;        //m_pBuffer = NULL;    }}void CSimpleLoopBufferEx::Clear(){    m_pNextRead = m_pBuffer;    m_pNextWrite = m_pBuffer;}bool CSimpleLoopBufferEx::Init(){    //m_pBuffer = SDNew char[nSize + 1];    CBlock* pBlock = CGateFactory::Instance()->CreateBlock();    if (!pBlock)        return false;    m_pBuffer = pBlock->GetBuff();    if (NULL == m_pBuffer)    {        return false;    }    m_pNextRead = m_pBuffer;    m_pNextWrite = m_pBuffer;    //m_pEnd = m_pBuffer + nSize + 1;    m_pEnd = m_pBuffer + pBlock->GetBuffLen();    return true;}INT32 CSimpleLoopBufferEx::PushBack(const CHAR *pData, INT32 nLen){    CHAR* poRead = m_pNextRead;    if (m_pNextWrite >= poRead)    {        //  1、尾部指针减去写起始位置小于 nLen         //  2、读的起始位置减去内存首地址 小于nLen        //  内存空间不够,不能将数据写入缓存        /*                          == 内存模型 ==                   (empty)             m_pNextRead         m_pNextWrite       (empty)        |----------------------------------|--------------------|---------------------|        */        INT32 nRight = m_pEnd - m_pNextWrite;        INT32 nLeft = poRead - m_pBuffer;        if (nLeft + nRight <= nLen)        {            return -1;        }    }    else    {        /*                          == 内存模型 ==                                       m_pNextWrite (empty) m_pNextRead        |----------------------------------|--------------------|---------------------|        */        if (poRead - m_pNextWrite <= nLen)        {            return -2;        }    }    //     else  //  m_pNextWrite == m_pNextRead    //     {    //         if (m_pNextWrite > m_pBuffer)    //         {    //             m_oMutex.Unlock();    //             return -3;    //         }    //     }    if (m_pEnd - m_pNextWrite > nLen)    {        memcpy(m_pNextWrite, pData, nLen);        m_pNextWrite += nLen;    }    else if (m_pEnd - m_pNextWrite == nLen)    {        memcpy(m_pNextWrite, pData, nLen);        m_pNextWrite = m_pBuffer;    }    else    {        INT32 nStartLen = m_pEnd - m_pNextWrite;        memcpy(m_pNextWrite, pData, nStartLen);        memcpy(m_pBuffer, pData + nStartLen, nLen - nStartLen);        m_pNextWrite = m_pBuffer + nLen - nStartLen;    }    return nLen;}INT32 CSimpleLoopBufferEx::PopFront(CHAR * &pBuf, INT32 nLen, CHAR* szData){    CHAR* poNextWrite = m_pNextWrite;    if (poNextWrite == m_pNextRead)    {        EXLOG_ERROR << "[%s:%d]:PopFront failed! ErrCode:-1";        return -1;    }    if (poNextWrite > m_pNextRead)    {        /*                          == 内存模型 ==                   (empty)             m_pNextRead         m_pNextWrite       (empty)        |----------------------------------|--------------------|---------------------|        */        if (poNextWrite - m_pNextRead < nLen)        {            EXLOG_ERROR << "PopFront failed! ErrCode:-2";            return -2;        }    }    else    {        /*                          == 内存模型 ==                                      m_pNextWrite (empty) m_pNextRead        |----------------------------------|--------------------|---------------------|        */        INT32 nRight = m_pEnd - m_pNextRead;        INT32 nLeft = poNextWrite - m_pBuffer;        if (nLeft + nRight < nLen)        {            EXLOG_ERROR << "[%s:%d]:PopFront failed! ErrCode:-3";            return -3;        }    }    if (m_pEnd - m_pNextRead > nLen)    {        memcpy(szData, m_pNextRead, nLen);        m_pNextRead += nLen;    }    else if (m_pEnd - m_pNextRead == nLen)    {        memcpy(szData, m_pNextRead, nLen);        m_pNextRead = m_pBuffer;    }    else    {        INT32 nStartLen = m_pEnd - m_pNextRead;        memcpy(szData, m_pNextRead, nStartLen);        memcpy(szData + nStartLen, m_pBuffer, nLen - nStartLen);        m_pNextRead = m_pBuffer + nLen - nStartLen;    }    return nLen;}bool CSimpleLoopBufferEx::DiscardFront(INT32 nLen){    CHAR* poNextWrite = m_pNextWrite;    if (poNextWrite == m_pNextRead)    {        EXLOG_ERROR << "[%s:%d]:PopFront failed! ErrCode:-1";        return false;    }    if (poNextWrite > m_pNextRead)    {        /*                          == 内存模型 ==        (empty)             m_pNextRead         m_pNextWrite       (empty)        |----------------------------------|--------------------|---------------------|        */        if (poNextWrite - m_pNextRead < nLen)        {            EXLOG_ERROR << "[%s:%d]:PopFront failed! ErrCode:-2";            return false;        }    }    else    {        /*                          == 内存模型 ==        m_pNextWrite (empty) m_pNextRead        |----------------------------------|--------------------|---------------------|        */        INT32 nRight = m_pEnd - m_pNextRead;        INT32 nLeft = poNextWrite - m_pBuffer;        if (nLeft + nRight < nLen)        {            EXLOG_ERROR << "[%s:%d]:PopFront failed! ErrCode:-3";            return false;        }    }    if (m_pEnd - m_pNextRead > nLen)    {        m_pNextRead += nLen;    }    else if (m_pEnd - m_pNextRead == nLen)    {        m_pNextRead = m_pBuffer;    }    else    {        INT32 nStartLen = m_pEnd - m_pNextRead;        m_pNextRead = m_pBuffer + nLen - nStartLen;    }    return true;}INT32 CSimpleLoopBufferEx::GetFreeSpare(){    CHAR* poRead = m_pNextRead;    if (m_pNextWrite >= poRead)    {        //  1、尾部指针减去写起始位置小于 nLen         //  2、读的起始位置减去内存首地址 小于nLen        //  内存空间不够,不能将数据写入缓存        /*                          == 内存模型 ==        (empty)             m_pNextRead         m_pNextWrite       (empty)        |----------------------------------|--------------------|---------------------|        */        INT32 nRight = m_pEnd - m_pNextWrite;        INT32 nLeft = poRead - m_pBuffer;        return nLeft + nRight;    }    else    {        /*                          == 内存模型 ==        m_pNextWrite (empty) m_pNextRead        |----------------------------------|--------------------|---------------------|        */        return poRead - m_pNextWrite;    }}INT32 CSimpleLoopBufferEx::GetData(INT32 nReadOffSet, INT32 nLen, CHAR* szData){    //指针偏移    CHAR* poNextWrite = m_pNextWrite;    if (poNextWrite == m_pNextRead)    {        EXLOG_ERROR << "[%s:%d]:PopFront failed! ErrCode:-1";        return -1;    }    CHAR *pReadOffSet = m_pNextRead + nReadOffSet;    if (poNextWrite > m_pNextRead)    {        /*                          == 内存模型 ==        (empty)             m_pNextRead         m_pNextWrite       (empty)        |----------------------------------|--------------------|---------------------|        */        if (pReadOffSet >= m_pNextWrite)        {            EXLOG_ERROR << "GetData failed! ErrCode:-2";            return -2;        }    }    else    {        /*                          == 内存模型 ==        m_pNextWrite (empty) m_pNextRead        |----------------------------------|--------------------|---------------------|        */        if (pReadOffSet < m_pEnd)        {        }        else if (pReadOffSet == m_pEnd)        {            pReadOffSet = m_pBuffer;            if (pReadOffSet >= m_pNextWrite)            {                EXLOG_ERROR << "GetData failed! ErrCode:-3";                return -3;            }        }        else {            pReadOffSet = m_pBuffer + nReadOffSet - (m_pEnd - m_pNextRead);            if (pReadOffSet >= m_pNextWrite)            {                EXLOG_ERROR << "GetData failed! ErrCode:-4";                return -4;            }        }    }    //拷贝数据    if (poNextWrite > pReadOffSet)    {        /*                          == 内存模型 ==        (empty)             m_pNextRead         m_pNextWrite       (empty)        |----------------------------------|--------------------|---------------------|        */        if (poNextWrite - pReadOffSet < nLen)        {            EXLOG_ERROR << "PopFront failed! ErrCode:-5";            return -5;        }    }    else    {        /*                          == 内存模型 ==        m_pNextWrite (empty) m_pNextRead        |----------------------------------|--------------------|---------------------|        */        INT32 nRight = m_pEnd - pReadOffSet;        INT32 nLeft = poNextWrite - m_pBuffer;        if (nLeft + nRight < nLen)        {            EXLOG_ERROR << "[%s:%d]:PopFront failed! ErrCode:-6";            return -6;        }    }    if (m_pEnd - pReadOffSet > nLen)    {        memcpy(szData, pReadOffSet, nLen);        //pReadOffSet += nLen;    }    else if (m_pEnd - pReadOffSet == nLen)    {        memcpy(szData, pReadOffSet, nLen);        //pReadOffSet = m_pBuffer;    }    else    {        INT32 nStartLen = m_pEnd - pReadOffSet;        memcpy(szData, pReadOffSet, nStartLen);        memcpy(szData + nStartLen, m_pBuffer, nLen - nStartLen);        //m_pNextRead = m_pBuffer + nLen - nStartLen;    }    return nLen;}
]]>
- - - - - <p>头文件</p> -<pre><code class="hljs c++"><span class="hljs-meta">#<span class="hljs-keyword">ifndef</span> _SIMPLE_LOOP_BUFFER_H__</span> -<spa - - - - - - - - - -
- - - linux fork函数详解 - - https://blog.genge.cc/2020/01/23/linux%20fork%E5%87%BD%E6%95%B0%E8%AF%A6%E8%A7%A3/ - 2020-01-23T07:12:45.000Z - 2020-01-23T07:12:45.000Z - - 函数原型: pid_tfork(void)
  1. 参数:不需要参数
  2. 需要的头文件 <sys/types.h><unistd.h>
  3. 返回值分两种情况:
    • 返回0表示成功创建子进程,并且接下来进入子进程执行流程
    • 返回PID(>0),成功创建子进程,并且继续执行父进程流程代码
    • 返回非正数(<0),创建子进程失败,失败原因主要有:
      • 进程数超过系统所能创建的上限,errno会被设置为EAGAIN
      • 系统内存不足,errno会被设置为ENOMEM

地址空间

使用 fork() 函数得到的子进程是父进程的一个复制品,它从父进程处继承了整个进程的地址空间:包括进程上下文(进程执行活动全过程的静态描述)、进程堆栈、打开的文件描述符、信号控制设定、进程优先级、进程组号等。子进程所独有的只有它的进程号,计时器等(只有小量信息)。因此,使用 fork() 函数的代价是很大的。

共享方式

实际上,更准确来说,Linux 的 fork() 使用是通过写时拷贝 (copy- on-write) 实现。写时拷贝是一种可以推迟甚至避免拷贝数据的技术。内核此时并不复制整个进程的地址空间,而是让父子进程共享同一个地址空间。只用在需要写入的时候才会复制地址空间,从而使各个进行拥有各自的地址空间。也就是说,资源的复制是在需要写入的时候才会进行,在此之前,只有以只读方式共享。

执行顺序

创建新进程成功后,系统中出现两个基本完全相同的进程,这两个进程执行没有固定的先后顺序,哪个进程先执行要看系统的进程调度策略。

linux有个类似的函数vfork():函数表面看起来都一样,但是它保证子进程先运行,在它调用 exec(进程替换) 或 exit(退出进程)之后父进程才可能被调度运行。子进程共享父进程的地址空间(准确来说,在调用 exec(进程替换) 或 exit(退出进程) 之前与父进程数据是共享的), vfork() 创建的子进程会执行完后,才到父进程执行。

区别

子进程与父进程的区别在于:

  1. 除了文件锁以外,其他的锁都会被继承
  2. 各自的进程ID和父进程ID不同
  3. 子进程的未决告警被清除;
  4. 子进程的未决信号集设置为空集。

孤儿进程、僵尸进程

fork系统调用之后,父子进程将交替执行,执行顺序不定。如果父进程先退出,子进程还没退出那么子进程的父进程将变为init进程(托孤给了init进程)。(注:任何一个进程都必须有父进程)如果子进程先退出,父进程还没退出,那么子进程必须等到父进程捕获到了子进程的退出状态才真正结束,否则这个时候子进程就成为僵进程(僵尸进程:只保留一些退出信息供父进程查询)

多线程进程的Fork调用

坑大,面试可能会问道,工作中也要小心使用
云风 BLOG: 极不和谐的 fork 多线程程序
讲的主要是当前的进程processA (thread a/b/c)的当前子线程thread a调用fork后,会创建子进程,但是只是复制了thread a,总结一句就是所有父进程中别的线程,到了子进程中都是突然蒸发掉的。这样会导致各种死锁问题,以及各种数据不一致等问题。最好的办法是在多线程进程里不是用fork。如果非使用不可,尽量fork完毕后直接exec,不调用任何其他除了fork之外的函数。exec可以覆盖内存空间,可以解决所有关于锁的问题。
还有一些文章可以看看:

  1. 谨防fork与锁之间的深坑 - CSDN博客
  2. 子进程继承父进程中互斥锁的讨论 - CSDN博客
  3. 在多线程中使用fork函数导致死锁,以及解决方案 - CSDN博客
]]>
- - - - - <h3 id="函数原型:-pid-tfork-void"><a href="#函数原型:-pid-tfork-void" class="headerlink" title="函数原型: pid_t fork(void)"></a>函数原型: <code>pid_t fork(v - - - - - - - - - -
- - - 各类APP排行榜实现 - - https://blog.genge.cc/2020/01/23/%E5%90%84%E7%B1%BBAPP%E6%8E%92%E8%A1%8C%E6%A6%9C%E5%AE%9E%E7%8E%B0/ - 2020-01-23T07:12:45.000Z - 2020-01-23T07:12:45.000Z - - 需求背景
  1. 查看TopN的用户排名
  2. 查看自己的排名
  3. 用户积分变更后,排名及时更新

实现方案

方案一 利用MYSQL 排序

利用MySQL来实现,存放一张用户积分表user_score
取前top N,自己的排名都可以通过简单的sql语句搞定。
算法简单,利用sql的功能,不需要其他复杂逻辑,对于数据量比较少、性能要求不高,可以使用。但是对于海量数据,性能是无法接受的。可能会导致全局锁表之类的问题。

方案二 内存数组排序

在内存中预分配所要排名用户大小的数组,所有的积分排名变更基于此移动元素,成熟排序算法有最小/大堆、快速排序等,这种方案优点是当数据量小的时候,简单快捷,容易实现,不需要其他任何组件支持,但是当面对海量数据的时候数组空间占用可能不太现实

方案三 利用GCC库支持

具体的,用GCC的pb_ds库中有assoc_container来进行实现。
参考tree_order_statistics.cc

#include <cassert>#include <ext/pb_ds/assoc_container.hpp>#include <ext/pb_ds/tree_policy.hpp>using namespace std;using namespace pb_ds;using namespace pb_ds;// A red-black tree table storing ints and their order// statistics. Note that since the tree uses// tree_order_statistics_node_update as its update policy, then it// includes its methods by_order and order_of_key.typedeftree<  int,  null_mapped_type,  less<int>,  rb_tree_tag,  // This policy updates nodes' metadata for order statistics.  tree_order_statistics_node_update>set_t;int main(){  // Insert some entries into s.  set_t s;  s.insert(12);  s.insert(505);  s.insert(30);  s.insert(1000);  s.insert(10000);  s.insert(100);  // The order of the keys should be: 12, 30, 100, 505, 1000, 10000.  assert(*s.find_by_order(0) == 12);  assert(*s.find_by_order(1) == 30);  assert(*s.find_by_order(2) == 100);  assert(*s.find_by_order(3) == 505);  assert(*s.find_by_order(4) == 1000);  assert(*s.find_by_order(5) == 10000);  assert(s.find_by_order(6) == s.end());  // The order of the keys should be: 12, 30, 100, 505, 1000, 10000.  assert(s.order_of_key(10) == 0);  assert(s.order_of_key(12) == 0);  assert(s.order_of_key(15) == 1);  assert(s.order_of_key(30) == 1);  assert(s.order_of_key(99) == 2);  assert(s.order_of_key(100) == 2);  assert(s.order_of_key(505) == 3);  assert(s.order_of_key(1000) == 4);  assert(s.order_of_key(10000) == 5);  assert(s.order_of_key(9999999) == 6);  // Erase an entry.  s.erase(30);  // The order of the keys should be: 12, 100, 505, 1000, 10000.  assert(*s.find_by_order(0) == 12);  assert(*s.find_by_order(1) == 100);  assert(*s.find_by_order(2) == 505);  assert(*s.find_by_order(3) == 1000);  assert(*s.find_by_order(4) == 10000);  assert(s.find_by_order(5) == s.end());  // The order of the keys should be: 12, 100, 505, 1000, 10000.  assert(s.order_of_key(10) == 0);  assert(s.order_of_key(12) == 0);  assert(s.order_of_key(100) == 1);  assert(s.order_of_key(505) == 2);  assert(s.order_of_key(707) == 3);  assert(s.order_of_key(1000) == 3);  assert(s.order_of_key(1001) == 4);  assert(s.order_of_key(10000) == 4);  assert(s.order_of_key(100000) == 5);  assert(s.order_of_key(9999999) == 5);  return 0;}

存取效率都可以达到O(log(n)),不足就是程序重启后数据会丢失。还是对所有的用户积分,没必要。
而且是有依赖,不方便扩展,实现不了复杂的需求,

方案四 实现排序树

大致实现思路如下:

  我们可以把[0, 1,000,000)作为一级区间;再把一级区间分为两个2级区间[0, 500,000), [500,000, 1,000,000),然后把二级区间二分为4个3级区间[0, 250,000), [250,000, 500,000), [500,000, 750,000), [750,000, 1,000,000),依此类推,最终我们会得到1,000,000个21级区间[0,1), [1,2) … [999,999, 1,000,000)。这实际上是把区间组织成了一种平衡二叉树结构,根结点代表一级区间,每个非叶子结点有两个子结点,左子结点代表低分区间,右子结点代表高分区间。树形分区结构需要在更新时保持一种不变量,非叶子结点的count值总是等于其左右子结点的count值之和。

  以后,每次用户积分有变化所需要更新的区间数量和积分变化量有关系,积分变化越小更新的区间层次越低。总体上,每次所需要更新的区间数量是用户积分变量的log(n)级别的,也就是说如果用户积分一次变化在百万级,更新区间的数量在二十这个级别。在这种树形分区积分表的辅助下查询积分为s的用户排名,实际上是一个在区间树上由上至下、由粗到细一步步明确s所在位置的过程。比如,对于积分499,000,我们用一个初值为0的排名变量来做累加;首先,它属于1级区间的左子树[0, 500,000),那么该用户排名应该在右子树[500,000, 1,000,000)的用户数count之后,我们把该count值累加到该用户排名变量,进入下一级区间;其次,它属于3级区间的[250,000, 500,000),这是2级区间的右子树,所以不用累加count到排名变量,直接进入下一级区间;再次,它属于4级区间的…;直到最后我们把用户积分精确定位在21级区间[499,000, 499,001),整个累加过程完成,得出排名!

  虽然,本算法的更新和查询都涉及到若干个操作,但如果我们为区间的from_score和to_score建立索引,这些操作都是基于键的查询和更新,不会产生表扫描,因此效率更高。另外,本算法并不依赖于关系数据模型和SQL运算,可以轻易地改造为NoSQL等其他存储方式,而基于键的操作也很容易引入缓存机制进一步优化性能。进一步,我们可以估算一下树形区间的数目大约为2,000,000,考虑每个结点的大小,整个结构只占用几十M空间。所以,我们完全可以在内存建立区间树结构,并通过user_score表在O(n)的时间内初始化区间树,然后排名的查询和更新操作都可以在内存进行。一般来讲,同样的算法,从数据库到内存算法的性能提升常常可以达到10^5以上;因此,本算法可以达到非常高的性能。

  算法特点

  优点:结构稳定,不受积分分布影响;每次查询或更新的复杂度为积分最大值的O(log(n))级别,且与用户规模无关,可以应对海量规模;不依赖于SQL,容易改造为NoSQL或内存数据结构。

  缺点:算法相对更复杂。

方案五 实现跳表排序

skip list是链表的一种特殊形式,对链表的一种优化;保证INSERT和REMOVE操作是O(logn),而通用链表的复杂度为O(n);
优点:实现较简单,效率基本上O(log(N))
缺点:当达到亿级别时的数据时,性能会急剧下降

方案六 利用redis特新实现

其实redis底层还是使用跳表实现排序的,只是将接口都封装好了,使用接口也比较完善,稳定。

redis的zset天生是用来做排行榜的、好友列表, 去重, 历史记录等业务需求。接口使用非常简单。接口非常丰富,基本上需要的实现都能满足,说明如下:

ZAdd/ZRem是O(log(N)),ZRangeByScore/ZRemRangeByScore是O(log(N)+M),N是Set大小,M是结果/操作元素的个数。

ZSET的实现用到了两个数据结构:hash table 和 skip list(跳跃表),其中hash table是具体使用redis中的dict来实现的,主要是为了保证查询效率为O(1) ,而skip list(跳跃表)主要是保证元素有序并能够保证INSERT和REMOVE操作是O(logn)的复杂度。

优点:基于redis开发,速度快;使用redis相关特性

缺点:当达到亿级别时的数据时,性能会急剧下降

来实现排行榜的方法很多,可以根据自己的具体需求,参考选用。

方案七 其实只需要TopN的排名,大于N的排名并不需要精确排名计算

基于此,假设我们游戏内只需要排前100名,这里我们只需要维护一个100大小的数组

  1. 当元素A需要参与排序的时候,与数组中最小的积分进行比较,如果能进100名,那么将第100剔除,将A加入,并记录最小元素,这样就完成了积分上涨的情况
  2. 还有一种就是已经在前100名中元素的积分发生变化下降,那么需要在前100名后找出可以进排行榜的元素,这种情况比较麻烦,可以使用最大堆保存剩下所有用户的数据,当需要找出能替换进入排行榜的元素就非常快logn,选择特定数据结构非常重要
  3. 既然大于N的用户不需要精确排名,那么怎么样估算大概排名呢?一般做法是按照数值区间建立所若干个桶,比如我们预计要排名的那一个数据的最大值能到1W。我建立0-10, 10-100,100-1000, 1000-2000, 2000-5000, 5000-10000 这样6个桶,每个桶里面记录分值在这个桶对应的区间内,有多少个玩家。 比如
    0-10, 10人
    10-100,20人
    100-1000,30人
    1000-2000, 40人
    2000-5000, 50人
    5000-10000, 60人
    那么如果一个玩家 是 1234分,那么他的排名就超过了 (10 + 20 + 30)/ (10 + 20 + 30 + 40 + 50 + 60)这个百分比的玩家(所以桶分的越细,后面的排名越精确)
    实质就是按照分区间记录区间内元素个数,从而估算大概排名,因此数值区间越小,估算约精确。
]]>
- - - - - <h2 id="需求背景"><a href="#需求背景" class="headerlink" title="需求背景"></a>需求背景</h2><ol> -<li>查看TopN的用户排名</li> -<li>查看自己的排名</li> -<li>用户积分变更后,排名及时更新</li - - - - - - - - - -
- - - Timer 定时器技术分享 - - https://blog.genge.cc/2020/01/23/%E6%B7%B1%E5%85%A5%E6%B5%85%E5%87%BA%E6%97%B6%E9%97%B4%E8%BD%AETimer%E5%AE%9A%E6%97%B6%E5%99%A8/ - 2020-01-23T07:12:45.000Z - 2020-01-23T07:12:45.000Z - - 说点废话

不管是客户端Client还是服务器Server,不论你是从事游戏行业还是互联网行业,在技术上总会涉及到定时器。虽然有的框架系统已经帮你实现,并且提供完美API供你使用,但你真的了解定时器吗?我们不仅要知道如何使用正确的Timer,还得明白定时器的实现原理,要知其所以然。

理解定时器

使用者角度分类:

  1. 周期性定时器

    1. 使用 TCP 长连接时,客户端需要定时向服务端发送心跳请求
    2. 游戏内系统每日重置功能
    3. 体力回复
    4. ….
  2. 非周期性定时器

    1. 玩法活动定时开启、关闭

当然,大部分非周期性定时器都可以使用周期性定时器实现,即执行一次后立即调用Remove接口即可

定时器像水和空气一般,普遍存在于各个场景中,一般定时任务的形式表现为:经过固定时间后触发、按照固定频率周期性触发、在某个时刻触发。定时器是什么?可以理解为这样一个数据结构:存储一系列的任务集合,并且 Deadline 越接近的任务,拥有越高的执行优先级

支持以下几种操作:

  1. Add New TimerTask 添加新的定时器
  2. Kill Or Remove TimerTask 取消或者移除既有定时器任务
  3. Run 执行

判断一个TimerTask是否到期,基本会采用轮询的方式,每隔一个时间片tickDuration去检查最近的任务是否到期。

说到底,定时器还是靠线程轮询实现的。

现在知道Timer是靠轮询来实现的,那么中间应该采用那种数据结构呢?采用不同的数据结构实现,其性能也大不一样!
现在主要有如下几种:List链表、Heap最小堆、时间轮、分级时间轮,其中时间轮的实质为Hash表。

数据结构

双向有序链表

AddTimer O(N) 很容易理解,按照 expireTime 查找合适的位置即可;KillTimer O(1) ,任务在 Kill 时,会持有自己节点的引用,所以不需要查找其在链表中所在的位置,即可实现当前节点的删除;RunTimer O(1),由于整个双向链表是基于 expireTime 有序的,所以调度器只需要轮询第一个任务即可。

最小堆

最小堆指的是满足除了根节点以外的每个节点都不小于其父节点的堆。这样,堆中的最小值就存放在根节点中,并且在以某个结点为根的子树中,各节点的值都不小于该子树根节点的值。一个最小堆的例子如下图:
最小堆

明显的,最小堆添加新元素或者删除节点效率为O(lgn), root节点expireTime最小,执行优先级最高,因此复杂度为O(1)

如果程序中的定时器数量比较少,基于最小堆的定时器一般可以满足需求,且实现简单。

时间轮

时间轮的实质为哈希环HashTable,每个定时器任务根据对其expireTime哈希,得到对应的位置index,复杂度为O(1)
时间轮

性能比较:

实现方式AddTimerKillTimerRunTimer
基于链表O(1)O(n)O(n)
基于排序链表O(n)O(1)O(1)
基于最小堆O(lgn)O(lgn)O(1)
基于时间轮O(1)O(1)O(1)

现在看起来我们选择时间轮来实现就行了,是否这样就完事了?

着重分析时间轮

如果需要支持的定时器范围非常的大,上面的实现方式则不能满足这样的需求。因为这样将消耗非常可观的内存,假设需要表示的定时器范围为:0 – 2^3-1ticks,则简单时间轮需要 2^32 个元素空间,这对于内存空间的使用将非常的庞大。也许可以降低定时器的精度,使得每个 Tick 表示的时间更长一些,但这样的代价是定时器的精度将大打折扣。

现在的问题是,度量定时器的粒度,只能使用唯一粒度吗?想想日常生活中常遇到的水表,如下图:
水表

钟表:
水表

分级时间轮同样如此,每级时间轮所代表的粒度精度都不一样,这样结合起来,既能够保证定时器的精度,也能以较小内存代价表示范围更大更多的定时器。

简单时间轮: 一个齿轮,每个齿轮保存一个超时的node链表。一个齿轮表示一个时间刻度,比如钟表里面一小格代表一秒,钟表的秒针每次跳一格。假设一个刻度代表10ms,则2^32 个格子可表示1.36年,2^16个格子可表示10.9分钟。当要表示的时间范围较大时,空间复杂度会大幅增加。

分级时间轮: 类似于水表,当小轮子里的指针转动满一圈后,上一级轮子的指针进一格。 采用五个轮子每个轮子为一个简单时间轮,大小分别为 2^8, 2^6, 2^6, 2^6, 2^6,所需空间:2^8 + 2^6 + 2^6 + 2^6 + 2^6 = 512, 可表示的范围为 0 – 2^8 * 2^6 * 2^6* 2^6* 2^6 = 2^32 。

分级时间轮简洁图
分级时间轮

熟知的Linux系统内核,定时器实现方式就是分级时间轮
Linux内核

具体实现

wheel_timer_mgr.h

class CWheelTimerModule;class ITimerMgr;class CWheelTimer;typedef std::list<CWheelTimer*> TListTimer;enum ETimerType {     ETIMER_ONCE,     ETIMER_CIRCLE };///定时器最小精度 1/10秒 (100毫秒)static const int  WHEEL_TIMER_MIN_PRECISION = 100;/*** @brief 基于分级的时间轮定时器, 精度约定为十分之一秒* 注意,注册的Timer精度必须为约定的精度倍数*/class CWheelTimer{public:    CWheelTimer();    CWheelTimer(CWheelTimerModule& oModule);    ~CWheelTimer();    /**    * @brief 启动定时器    * @param nInterval: 传入的是毫秒, 约定必须是十分之一秒(100ms)的倍数    * @return     */    void Start(ITimerMgr* pITimer, unsigned int nId, unsigned nInterval, int nDelay, ETimerType eTimerType = ETIMER_CIRCLE);    /**    * @brief 停止定时器    */    void Stop();private:    /**    * @brief 定时器被触发    */    void OnTrigger(const UINT64 nNow);private:    friend class CWheelTimerModule;    CWheelTimerModule&m_oModule;    ITimerMgr*m_pTimerMgr = nullptr;    ETimerTypem_eTimerType;    unsigned intm_nTimerId = 0;    unsigned intm_nInterval = -1;    //ms    unsigned long longm_llExpireTime = 0;  //ms    intm_nVecIndex = 0;    TListTimer::iteratorm_listIter;};/*** @brief 定时器管理器接口, 派生类继承使用*/class ITimerMgr{public:    virtual  ~ITimerMgr();    virtual void  OnTimer(unsigned int nId) = 0;    //interval 时间精度 ms    void  SetTimer(unsigned int nId, int nInterval, int nDelay = 0, ETimerType eTimeType = ETIMER_CIRCLE);    void  KillTimer(unsigned int nId);    bool  IsTimerExist(unsigned int nId);;protected:    std::unordered_map<unsigned int, CWheelTimer*> m_mapTimer;};/*** @brief 全局定时器管理模块, 负责管理所有的定时器*/class CWheelTimerModule : public Storm::TSingleton<CWheelTimerModule>{    friend class Storm::TSingleton<CWheelTimerModule>;    CWheelTimerModule();public:    voidAddTimer(CWheelTimer* pTimer);    voidRemoveTimer(CWheelTimer* pTimer);    /**    * @brief 驱动所有的定时器    */    voidRun();    static UINT64GetCurMillisecs();    /**    * @brief 修正精度     * @param nSrcTime     * @return 传入的时间除以当前约定最小定时器精度    */    static UINT64HandlePrecision(const UINT64 nSrcTime);private:    int_Cascade(int nOffset, int nIndex);private:    std::vector<TListTimer>m_vecTimerList;    //notice: precision=100ms not 1ms    UINT64m_llCheckTime;};/*** @brief 定时器工厂 */class CTimerFactory: public Storm::TSingleton<CTimerFactory>, public CNoncopy{    friend class Storm::TSingleton<CTimerFactory>;    CTimerFactory();    virtual~CTimerFactory();public:     CWheelTimer*CreateCTimer();    voidReleaseCTimer(CWheelTimer* pTimer);private:    CSTObjectPool<CWheelTimer>    m_oCTimerPool;}

wheel_timer_mgr.cpp

#if 0     #define TVN_BITS 6    #define TVR_BITS 8    #define TVN_SIZE (1 << TVN_BITS)    #define TVR_SIZE (1 << TVR_BITS)    #define TVN_MASK (TVN_SIZE - 1)    #define TVR_MASK (TVR_SIZE - 1)    #define OFFSET(N) (TVR_SIZE + (N) *TVN_SIZE)    #define INDEX(V, N) ((V >> (TVR_BITS + (N) *TVN_BITS)) & TVN_MASK)#else     static const int TVN_BITS = 6;    static const int TVR_BITS = 8;    static const int TVN_SIZE = (1 << TVN_BITS);    static const int TVR_SIZE = (1 << TVR_BITS);    static const int TVN_MASK = (TVN_SIZE - 1);    static const int TVR_MASK = (TVR_SIZE - 1);    int OFFSET(int N) { return (TVR_SIZE + (N)*TVN_SIZE); }    int INDEX(unsigned long long V, int N)    {        return ((V >> (TVR_BITS + (N)*TVN_BITS)) & TVN_MASK);    }#endifCWheelTimer::CWheelTimer()    :m_oModule(CWheelTimerModule::GetInstance())    , m_nVecIndex(-1){}CWheelTimer::CWheelTimer(CWheelTimerModule& oModule)    : m_oModule(oModule)    , m_nVecIndex(-1){}CWheelTimer::~CWheelTimer(){    Stop();}void CWheelTimer::Start(ITimerMgr* pTimerMgr, unsigned int nId, unsigned nInterval, int nDelay, ETimerType eTimerType){    Stop();    //时间都修正为最小精度    if (nInterval < WHEEL_TIMER_MIN_PRECISION)    {        nInterval = WHEEL_TIMER_MIN_PRECISION;    }    m_nInterval = CWheelTimerModule::HandlePrecision(nInterval);    m_eTimerType = eTimerType;    m_pTimerMgr = pTimerMgr;    m_nTimerId = nId;    m_llExpireTime = CWheelTimerModule::HandlePrecision(nDelay + CWheelTimerModule::GetCurMillisecs());    m_oModule.AddTimer(this);}void CWheelTimer::Stop(){    if (m_nVecIndex != -1)    {        m_oModule.RemoveTimer(this);        m_nVecIndex = -1;    }}void CWheelTimer::OnTrigger(const UINT64 nNow){    if (m_eTimerType == ETIMER_CIRCLE)    {        m_llExpireTime = m_nInterval + nNow;        m_oModule.AddTimer(this);    }    else    {        m_nVecIndex = -1;    }    if (m_pTimerMgr != nullptr)    {        m_pTimerMgr->OnTimer(m_nTimerId);    }}//--------------------------------------------------------------------------------------------------------CWheelTimerModule::CWheelTimerModule(){    m_vecTimerList.resize(TVR_SIZE + 4 * TVN_SIZE);    m_llCheckTime = HandlePrecision(GetCurMillisecs());}void CWheelTimerModule::AddTimer(CWheelTimer* pTimer){    UINT64 llExpireTime = pTimer->m_llExpireTime;    INT64 llTimeDiff = pTimer->m_llExpireTime - m_llCheckTime;    if (llTimeDiff < 0)    {        pTimer->m_nVecIndex = m_llCheckTime & TVR_MASK;    }    else if (llTimeDiff < TVR_SIZE)    {        pTimer->m_nVecIndex = llExpireTime & TVR_MASK;    }    else if (llTimeDiff < 1 << (TVR_BITS + TVN_BITS))    {        pTimer->m_nVecIndex = OFFSET(0) + INDEX(llExpireTime, 0);    }    else if (llTimeDiff < 1 << (TVR_BITS + 2 * TVN_BITS))    {        pTimer->m_nVecIndex = OFFSET(1) + INDEX(llExpireTime, 1);    }    else if (llTimeDiff < 1 << (TVR_BITS + 3 * TVN_BITS))    {        pTimer->m_nVecIndex = OFFSET(2) + INDEX(llExpireTime, 2);    }    else    {        if (llTimeDiff > 0xffffffffUL)        {            llTimeDiff = 0xffffffffUL;            llExpireTime = llTimeDiff + m_llCheckTime;        }        pTimer->m_nVecIndex = OFFSET(3) + INDEX(llExpireTime, 3);    }    TListTimer& listTimer = m_vecTimerList[pTimer->m_nVecIndex];    listTimer.push_back(pTimer);    pTimer->m_listIter = listTimer.end();    --pTimer->m_listIter;}void CWheelTimerModule::RemoveTimer(CWheelTimer* pTimer){    TListTimer& listTimer = m_vecTimerList[pTimer->m_nVecIndex];    listTimer.erase(pTimer->m_listIter);}void CWheelTimerModule::Run(){    UINT64 nNow = HandlePrecision(GetCurMillisecs());    while (m_llCheckTime <= nNow)    {        //for every tick        int index = m_llCheckTime & TVR_MASK;        if (!index &&            !_Cascade(OFFSET(0), INDEX(m_llCheckTime, 0)) &&            !_Cascade(OFFSET(1), INDEX(m_llCheckTime, 1)) &&            !_Cascade(OFFSET(2), INDEX(m_llCheckTime, 2)))        {            _Cascade(OFFSET(3), INDEX(m_llCheckTime, 3));        }                ++m_llCheckTime;        TListTimer& listTimer = m_vecTimerList[index];        TListTimer listTmp;        listTmp.splice(listTmp.end(), listTimer);        for (auto itr = listTmp.begin(); itr != listTmp.end(); ++itr)        {            auto* pTimer = *itr;            if (pTimer != nullptr)            {                pTimer->OnTrigger(nNow);            }        }    }}int CWheelTimerModule::_Cascade(int nOffset, int nIndex){    TListTimer& listTimer = m_vecTimerList[nOffset + nIndex];    TListTimer listTemp;    listTemp.splice(listTemp.end(), listTimer);    for (auto itr = listTemp.begin(); itr != listTemp.end(); ++itr)    {        auto* pTimer = *itr;        if (pTimer != nullptr)        {            AddTimer(pTimer);        }    }    return nIndex;}UINT64 CWheelTimerModule::HandlePrecision(const UINT64 nSrcTime){    return nSrcTime / WHEEL_TIMER_MIN_PRECISION;}UINT64 CWheelTimerModule::GetCurMillisecs(){    auto llCurTime = CSysTime::Instance()->GetNowMliTime();    return llCurTime;}ITimerMgr::~ITimerMgr(){    for (auto it : m_mapTimer)    {        if (it.second != nullptr)        {            it.second->Stop();            CTimerFactory::Instance()->ReleaseCTimer(it.second);        }    }    m_mapTimer.clear();}void ITimerMgr::SetTimer(unsigned int nId, int nInterval, int nDelay ,ETimerType eTimeType){    if (IsTimerExist(nId))    {        EXLOG_DEBUG << "[RyzTimer]Timer Has Existed, Not Repeat Add, nId:" << nId;        return;    }    CWheelTimer* pTimer = CTimerFactory::Instance()->CreateCTimer();    if (nullptr == pTimer)    {        return;    }    m_mapTimer[nId] = pTimer;    pTimer->Start(this, nId, nInterval, nDelay, eTimeType);    EXLOG_DEBUG << "[RyzTimer]Add Timer nId:" << nId << ",nInterval:" << nInterval << ",nDelay:" << nDelay << ",eTimeType:" << eTimeType;}void ITimerMgr::KillTimer(unsigned int nId){    auto it = m_mapTimer.find(nId);    if (it != m_mapTimer.end())    {        // 释放后 在TimerManager中 不会再执行 不需要做其他的操作        it->second->Stop();        CTimerFactory::Instance()->ReleaseCTimer(it->second);        m_mapTimer.erase(it);    }}bool ITimerMgr::IsTimerExist(unsigned int nId) {     return m_mapTimer.find(nId) != m_mapTimer.end(); }CTimerFactory::CTimerFactory(){    m_oCTimerPool.Init(32, 8);}CTimerFactory::~CTimerFactory(){}CWheelTimer * CTimerFactory::CreateCTimer(){    CWheelTimer* pTimer = m_oCTimerPool.FetchObj();    return pTimer;}void CTimerFactory::ReleaseCTimer(CWheelTimer* pTimer){    if (nullptr != pTimer)    {        m_oCTimerPool.ReleaseObj(pTimer);    }}
]]>
- - - - - <h2 id="说点废话"><a href="#说点废话" class="headerlink" title="说点废话"></a>说点废话</h2><blockquote> -<p>不管是客户端<code>Client</code>还是服务器<code>Server</code> - - - - - - - - - -
- - - 全局唯一ID生成算法优化 - - https://blog.genge.cc/2020/01/23/%E5%85%A8%E5%B1%80%E5%94%AF%E4%B8%80ID%E7%94%9F%E6%88%90%E7%AE%97%E6%B3%95%E4%BC%98%E5%8C%96/ - 2020-01-23T07:12:42.000Z - 2020-01-23T07:12:42.000Z - -

前言

在进程启动前,一般会给每个进程静态分配一个唯一标识ID(ServerID或者PipeID)

struct SServerID{    UINT16 wPlatform = 0; ///< Platform   平台id     UINT16 wArea = 0;        ///< Area       区服id    UINT16 wType = 0;        ///< Type       服务器App类型    UINT16 wIndex = 0;       ///< Index      服务器App编号索引};

每个Role/Item/Hero/Mail 等创建的时候都会创建一个UUID来唯一标识,其中Mail_Uuid还有趋势递增的需求

Snowflake算法介绍

SnowFlake分布式生成Id算法由Twitter开源

SnowFlake算法生成id的结果是一个64bit大小的UINT64整数,它的结构如下图:
snowflake uuid-64bit

SnowFlake的优点:
- 整体上按照时间自增排序
- 整个分布式系统内不会产生ID碰撞(时间戳和自增序列以外的字段作区分), 并且效率较高(位运算),

SnowFlake每秒能够产生6.4万ID左右.(和5段位的配置位数有关)
UUID从高位到低位依次排列:

  • 第一段:39位, 当对于某一个时间点的时间戳差值(至少10年可用)
  • 第二段:3 位, 平台platform或大区id,比如QQ Android/QQ IOS/Wechat IOS 等(8)
  • 第三段:11位, 区服area id,对应的就是服务器小区的ID(2048)
  • 第四段:5 位, 服务器APP实例id index(32), Notice:appid需要在area范围内唯一
  • 第五段:6 位, 自增长id,也就是说在一个完整的毫秒时间内最多可以生成64个UUID

SnowFlake算法优化

目前算法设计缺陷

在实际服务器运行过程中,尤其在游戏服务器开发期间,大量用户注册,会有瞬间生成大量UUID的需求

之前服务器的做法是:

//遍历了一圈64个,等下一个毫秒生成if (m_nGlobalSeq == m_nMliSeq){    // 毫秒内序列溢出, 阻塞到下一个毫秒,获得新的时间戳    nCurTimestamp = WaitForNextMilli(m_nLastTimestamp);}/*** 阻塞到下一个毫秒,直到获得新的时间戳** @param lastTimestamp 上次生成ID的时间截* @return 当前时间戳*/UINT64 WaitForNextMilli(UINT64 lastTimestamp) const{    UINT64 nTimestamp = _GetNowMliTime();    while (nTimestamp <= lastTimestamp)    {        nTimestamp = _GetNowMliTime();    }    return nTimestamp;}

这里使用while强行等待到下一毫秒,相当于阻塞当前线程,从而成为热点函数,需要去优化

设计优化

  • 合理分配各字段占用bit(图中已经是调整过的)
  • 将生成uuid单独出一个独立全局Server组件,提供全局唯一UUID服务,这样除去高位39bit外理论上其他bit都可以当作自增位,而且可以提前生成很多uuid放在pool当中,有需要的进程从当中取,可以满足需求,Baidu在github上开源的UUID生成算法就是这样处理的
  • 我们取一个折中方案,只是在server中加一个Pool,存放提前生成好的UUIDs,每次业务端需要UUID的时候首先从Pool中取,如果取不到就走原来的流程
UINT64 GenId(){    if (!IsEmpty())    {        //从Pool中取        return PopFrontElement();    }    return NextId();}

既然存在Pool,那么就需要设计Pool中元素填充方案
首先将Pool设定一个合适的固定最大值 const UINT32 UUID_POOL_MAX_SIZE = 1 << 13; //uuid pool 最大数 8192
然后根据当前Pool的状态,来定时填充,每次填充的数量为当前毫秒内所有可以生成的UUID数
Fill_Pool.png

#include "uuid_pool_mgr.h"#include "uuid_generator.h"bool CUUIDPool::Init(){    FillUuidPool();    return true;}void CUUIDPool::OnTimer(unsigned int nId){    EXLOG_DEBUG << "[RyzUuid]CUUIDPool::OnTimer Old EUuidPoolState : " << nId;    CUUIDMaker::Instance()->FillPoolWithInMli();    FillUuidPool();}void CUUIDPool::FillUuidPool(){    EUuidPoolState eUuidDequeState = CUUIDMaker::Instance()->GetUuidDequeState();    uint32 nUpdateInterval = 5 * 60 * 1000;    switch (eUuidDequeState)    {    case EUuidPoolState_Empty:        nUpdateInterval = 1 * 60 * 1000;        break;    case EUuidPoolState_Not_Full_0_30_Per:        nUpdateInterval = 2 * 60 * 1000;        break;    case EUuidPoolState_Not_Full_30_70_Per:        nUpdateInterval = 3 * 60 * 1000;        break;    case EUuidPoolState_Not_Full_70_100_Per:        nUpdateInterval = 4 * 60 * 1000;        break;    case EUuidPoolState_Full:        nUpdateInterval = 5 * 60 * 1000;        break;    default:        nUpdateInterval = 5 * 60 * 1000;        break;    }    EXLOG_DEBUG << "[RyzUuid]CUUIDPool::OnTimer Now EUuidPoolState : " << eUuidDequeState;    SetTimer(eUuidDequeState, nUpdateInterval, nUpdateInterval, ETIMER_ONCE);}enum EUuidPoolState{    EUuidPoolState_Empty= 1,    EUuidPoolState_Not_Full_0_30_Per= 2,    EUuidPoolState_Not_Full_30_70_Per= 3,    EUuidPoolState_Not_Full_70_100_Per= 4,    EUuidPoolState_Full= 5,};//uuid_generator.h//将当前毫秒内的UUID全部生成并存到Pool中void FillPoolWithInMli(){    EUuidPoolState nCurState = GetUuidDequeState();    AtomicUInt64 nCurTimestamp = _GetNowMliTime();    while (nCurState != EUuidPoolState_Full)    {        UINT64 nNextUUId = NextId(false);        if (nNextUUId == 0)        {            break;        }        PushBackElement(nNextUUId);        if (nCurTimestamp != _GetNowMliTime())        {            break;        }    }}

是否这样就完美了呢?

并没有呢!!!
这个算法强制依赖时间递增,如果时间回拨怎么办?
目前的做法是直接throw new exception
分析时间回拨产生原因
第一:人物操作,在真实环境一般不会有那个傻逼干这种事情,所以基本可以排除。
第二:由于有些业务等需要,机器需要同步时间服务器(在这个过程中可能会存在时间回拨,查了下我们服务器一般在10ms以内(2小时同步一次))。 Ntp过程可能产生时间回拨。
第三:QA和策划测试过程中有需求怎么办?
解决办法:

  1. 将uuid_generation独立出来给其他server提供服务
  2. 当回拨时间小于XXms,就等时间追上来之后继续生成。 (XXms对业务没有什么影响)
  3. 当时间大于XXms时间我们通过更换AppId位来来解决回拨问题。

Talk is cheap, show you the code

#ifndef __UUID_GENERATOR_H__#define __UUID_GENERATOR_H__#include "gnsingleton.h"#include "gntype.h"#include "gnpipe.h"#include "gntime.h"#include "noncopy.h"#include "gnserverid.h"#include <assert.h>#include <mutex>#include <atomic>#include <chrono>#include <exception>#include <sstream>#include <deque>enum EUuidPoolState{    EUuidPoolState_Empty= 1,    EUuidPoolState_Not_Full_0_30_Per= 2,    EUuidPoolState_Not_Full_30_70_Per= 3,    EUuidPoolState_Not_Full_70_100_Per= 4,    EUuidPoolState_Full= 5,};//#define SNOWFLAKE_ID_MAKER_NO_LOCKclass CSnowflakeIdMaker : private CNoncopy{public:#ifdef SNOWFLAKE_ID_MAKER_NO_LOCK    typedef std::atomic<UINT32> AtomicUInt;    typedef std::atomic<UINT64> AtomicUInt64;#else    typedef UINT32 AtomicUInt;    typedef UINT64 AtomicUInt64;#endif    const UINT32 UUID_POOL_MAX_SIZE = 1 << 13;   //uuid pool 最大数 8192    const UINT64 START_EPOCH= 1541001600000LL;//开始时间截 (2018-11-01 00:00:00.000),修改此时间可调整可用时长                                   const UINT32 A_TIMESTAMP_BITS= 39;//时间戳所占的位数    const UINT32 B_PLATFORM_BITS= 3;//平台id所占的位数    const UINT32 C_AREA_BITS= 11;//区服id所占的位    const UINT32 D_APP_ID_BITS= 5;//app id所占的位数    const UINT32 E_INCR_SEQUENCE_BITS= 6;    //自增序列所占的位数    const UINT32 APP_ID_SHIFT= E_INCR_SEQUENCE_BITS;//APPID向左移位数    const UINT32 AREA_ID_SHIFT= E_INCR_SEQUENCE_BITS + D_APP_ID_BITS;//小区id向左移位数    const UINT32 PLATFORM_ID_SHIFT= E_INCR_SEQUENCE_BITS + D_APP_ID_BITS + C_AREA_BITS;//大区id向左移位数    const UINT32 TIME_STAMP_SHIFT= E_INCR_SEQUENCE_BITS + D_APP_ID_BITS + C_AREA_BITS + B_PLATFORM_BITS;//时间戳向左移位数    const UINT32 SEQUENCE_MASK= (1 << E_INCR_SEQUENCE_BITS) - 1;//生成序列的掩码    CSnowflakeIdMaker() : m_nPlatformId(0), m_nAreaId(0), m_nGlobalSeq(0), m_nLastTimestamp(0) {}    CSnowflakeIdMaker(const UINT32 nPlatId, const UINT32 nAreaId, const UINT32 nAppId)    {        Init(nPlatId, nAreaId, nAppId);    }    void Init(const UINT32 nPlatId, const UINT32 nAreaId, const UINT32 nAppId)    {        m_nPlatformId = nPlatId;        m_nAreaId = nAreaId;        m_nAppId = nAppId;    }    UINT64 GenId()    {        if (!IsEmpty())        {            return PopFrontElement();        }        return NextId();    }    /**    * 获得下一个ID (该方法是线程安全的)    * @param  bCanBlock 参数指定当前函数是否可以阻塞, 默认为true    * @return SnowflakeId    */    UINT64 NextId(bool bCanBlock = true)    {        using namespace  std;#ifdef SNOWFLAKE_ID_MAKER_NO_LOCK        static AtomicUInt64 nCurTimestamp{ 0 };#else        std::unique_lock<std::mutex> oLock{ m_oMutex };        AtomicUInt64 nCurTimestamp{ 0 };#endif        nCurTimestamp = _GetNowMliTime();        // 如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常        if (nCurTimestamp < m_nLastTimestamp)        {            std::ostringstream oSS;            oSS << "clock moved backwards.  Refusing to generate id for " << m_nLastTimestamp - nCurTimestamp << " milliseconds";            throw std::exception(std::runtime_error(oSS.str()));        }        m_nGlobalSeq = (m_nGlobalSeq + 1) & SEQUENCE_MASK;        // 为了使id递增+1均匀分布,这里seq跨毫秒也不清0        if (m_nLastTimestamp == nCurTimestamp)        {            //遍历了一圈64个,等下一个毫秒生成            if (m_nGlobalSeq == m_nMliSeq)            {                if (bCanBlock)                {                    // 毫秒内序列溢出, 阻塞到下一个毫秒,获得新的时间戳                    nCurTimestamp = WaitForNextMilli(m_nLastTimestamp);                }                else                {                    return 0;                }            }        }        else        {            m_nMliSeq = m_nGlobalSeq;        }#ifdef SNOWFLAKE_ID_MAKER_NO_LOCK        m_nLastTimestamp = nCurTimestamp.load();#else        m_nLastTimestamp = nCurTimestamp;#endif        // 移位并通过或运算拼到一起组成64位的ID        return ((nCurTimestamp - START_EPOCH) << TIME_STAMP_SHIFT)            | (m_nPlatformId << PLATFORM_ID_SHIFT)            | (m_nAreaId << AREA_ID_SHIFT)            | (m_nAppId << APP_ID_SHIFT)            | (m_nGlobalSeq);    }    EUuidPoolState GetUuidDequeState()     {        if (m_deUuidPool.empty())        {            return EUuidPoolState_Empty;        }        size_t nCurPoolSize = m_deUuidPool.size();        if (nCurPoolSize >= UUID_POOL_MAX_SIZE)        {            return EUuidPoolState_Full;        }        uint32 nCurPercent = nCurPoolSize * 100 / UUID_POOL_MAX_SIZE;        if (nCurPercent < 30)        {            return EUuidPoolState_Not_Full_0_30_Per;        }        else if (nCurPercent < 70)        {            return EUuidPoolState_Not_Full_30_70_Per;        }        else        {            return EUuidPoolState_Not_Full_70_100_Per;        }        return EUuidPoolState_Full;    }     void FillPoolWithInMli()    {        EUuidPoolState nCurState = GetUuidDequeState();        AtomicUInt64 nCurTimestamp = _GetNowMliTime();        while (nCurState != EUuidPoolState_Full)        {            UINT64 nNextUUId = NextId(false);            if (nNextUUId == 0)            {                break;            }                        PushBackElement(nNextUUId);            if (nCurTimestamp != _GetNowMliTime())            {                break;            }        }    }protected:    //判断pool是否为空    bool IsEmpty()    {        return m_deUuidPool.empty();    }    //返回pool队列中front 元素    UINT64 PopFrontElement()    {#ifdef SNOWFLAKE_ID_MAKER_NO_LOCK        #else        std::unique_lock<std::mutex> oLock{ m_oMutex };#endif        UINT64 nFrontElement = m_deUuidPool.front();        m_deUuidPool.pop_front();        return nFrontElement;    }    void PushBackElement(UINT64 nNextUUId)    {#ifdef SNOWFLAKE_ID_MAKER_NO_LOCK#else        std::unique_lock<std::mutex> oLock{ m_oMutex };#endif        m_deUuidPool.push_back(nNextUUId);    }    /**    * 返回以毫秒为单位的当前时间    *    * @return 当前时间(毫秒)    */    UINT64 _GetNowMliTime() const    {        if (0)        {            Storm::CSTDateTime oDateTime;            oDateTime.Now();            return oDateTime.EpochMilliSecs();        }        else        {            using namespace std;            auto nTimeNow = chrono::system_clock::now();            auto nDurationInMs = chrono::duration_cast<chrono::milliseconds>(nTimeNow.time_since_epoch());            return nDurationInMs.count();        }    }    /**    * 阻塞到下一个毫秒,直到获得新的时间戳    *    * @param lastTimestamp 上次生成ID的时间截    * @return 当前时间戳    */    UINT64 WaitForNextMilli(UINT64 lastTimestamp) const    {        UINT64 nTimestamp = _GetNowMliTime();        while (nTimestamp <= lastTimestamp)        {            nTimestamp = _GetNowMliTime();        }        return nTimestamp;    }private:#ifndef SNOWFLAKE_ID_MAKER_NO_LOCK    std::mutexm_oMutex;#endif    UINT32m_nPlatformId = 0;//平台id    UINT32m_nAreaId = 0;//区服id    UINT32m_nAppId = 0;//Appid    AtomicUIntm_nGlobalSeq{ 0 };//全局序列    AtomicUIntm_nMliSeq{ 0 };//每毫秒序列    AtomicUInt64m_nLastTimestamp{ 0 };//上次生成ID的时间截    std::deque<UINT64> m_deUuidPool;//uuid池 用于存放预生成uuids};/************************************************************************//* 负责生成全局唯一id                                                *//************************************************************************/class CUUIDMaker : public Storm::TSingleton<CUUIDMaker>{    friend class Storm::TSingleton<CUUIDMaker>;public:    bool Init(const UINT32 nParaA, const UINT32 nParaB, const UINT32 nParaC)    {        m_oIdMaker.Init(nParaA, nParaB, nParaC);        return true;    }        /// init with pipeid    bool Init(const UINT64 nPipeId)    {        using namespace Storm;        CServerID oServID(nPipeId);        m_oIdMaker.Init(oServID.GetPlat(), oServID.GetArea(), oServID.GetIndex());        return true;    }    UINT64 GenId()    {        return m_oIdMaker.GenId();    }    EUuidPoolState GetUuidDequeState()    {        return m_oIdMaker.GetUuidDequeState();    }    void FillPoolWithInMli()    {        m_oIdMaker.FillPoolWithInMli();    }    UINT64 GetCompareIdFromTime(UINT32 nTimeVal);    UINT64 GetTimeAddVal(UINT32 nTimeVal);    UINT32 GetTimeValFromUuid(UINT64 nUuid);private:    CSnowflakeIdMakerm_oIdMaker;};#define  GEN_GLOBAL_UUID()  CUUIDMaker::Instance()->GenId()#endif//gameserver #include "uuid_pool_mgr.h"#include "uuid_generator.h"bool CUUIDPool::Init(){    FillUuidPool();    return true;}void CUUIDPool::OnTimer(unsigned int nId){    EXLOG_DEBUG << "[RyzUuid]CUUIDPool::OnTimer Old EUuidPoolState : " << nId;    CUUIDMaker::Instance()->FillPoolWithInMli();    FillUuidPool();}void CUUIDPool::FillUuidPool(){    EUuidPoolState eUuidDequeState = CUUIDMaker::Instance()->GetUuidDequeState();    uint32 nUpdateInterval = 5 * 60 * 1000;    switch (eUuidDequeState)    {    case EUuidPoolState_Empty:        nUpdateInterval = 1 * 60 * 1000;        break;    case EUuidPoolState_Not_Full_0_30_Per:        nUpdateInterval = 2 * 60 * 1000;        break;    case EUuidPoolState_Not_Full_30_70_Per:        nUpdateInterval = 3 * 60 * 1000;        break;    case EUuidPoolState_Not_Full_70_100_Per:        nUpdateInterval = 4 * 60 * 1000;        break;    case EUuidPoolState_Full:        nUpdateInterval = 5 * 60 * 1000;        break;    default:        nUpdateInterval = 5 * 60 * 1000;        break;    }    EXLOG_DEBUG << "[RyzUuid]CUUIDPool::OnTimer Now EUuidPoolState : " << eUuidDequeState;    SetTimer(eUuidDequeState, nUpdateInterval, nUpdateInterval, ETIMER_ONCE);}

参考文章

分布式唯一id:snowflake算法思考 - 掘金
https://tech.meituan.com/2017/04/21/mt-leaf.html
GitHub - baidu/uid-generator: UniqueID generator

]]>
- - - - - <!-- toc --> - -<ul> -<li><a href="#%E5%89%8D%E8%A8%80">前言</a></li> -<li><a href="#Snowflake%E7%AE%97%E6%B3%95%E4%BB%8B%E7%BB%8D">Snowflake算法介绍< - - - - - - - - - -
- - - linux 系统信号和中断常识 - - https://blog.genge.cc/2020/01/23/linux%20%E7%B3%BB%E7%BB%9F%E4%BF%A1%E5%8F%B7%E5%92%8C%E4%B8%AD%E6%96%AD%E5%B8%B8%E8%AF%86/ - 2020-01-23T07:12:40.000Z - 2020-01-23T07:12:40.000Z - - 什么是中断
  1. 中断基本概念

    中断是指计算机在执行期间,系统内发生任何非寻常的或非预期的急需处理事件,使得CPU暂时中断当前正在执行的程序而转去执行相应的事件处理程序,待处理完毕后又返回原来被中断处继续执行或调度新的进程执行的过程。引起中断发生的事件被称为中断源。中断源向CPU发出的请求中断处理信号称为中断请求,而CPU收到中断请求后转到相应的事件处理程序称为中断响应。
    在有些情况下,尽管产生了中断源和发出了中断请求,但CPU内部的处理器状态、字PSW的中断允许位已被清除,从而不允许CPU响应中断。这种情况称为禁止中断。CPU禁止中断后只有等到PSW的中断允许位被重新设置后才能接收中断。禁止中断也称为关中断,PSW的中断允许位的设置也被称为开中断。开中断和关中断是为了保证某段程序执行的原子性。
    还有一个比较常用的概念是中断屏蔽。中断屏蔽是指在中断请求产生之后,系统有选择地封锁一部分中断而允许另一部分中断仍能得到响应。不过,有些中断请求是不能屏蔽甚至不能禁止的,也就是说,这些中断具有最高优先级,只要这些中断请求一旦提出,CPU必须立即响应。例如,电源掉电事件所引起的中断就是不可禁止和不可屏蔽的。

  2. 中断分类与等级

    根据系统对中断处理的需要,操作系统一般对中断进行分类并对不同的中断赋予不同的处理优先级,以便在不同的中断同时发生时,按轻重缓急进行处理。
    根据中断源产生的条件,可把中断分为外中断和内中断。外中断是指来自处理器和内存外部的中断,包括I/0设备发出的I/O中断、外部信号中断(例如用户键人ESC键)。各种定时器引起的时钟中断以及调试程序中设置的断点等引起的调试中断等。外中断在狭义上一般被称为中断。
    内中断主要指在处理器和内存内部产生的中断。内中断一般称为陷阱(trap)或异常。它包括程序运算引起的各种错误,如地址非法、校验错、页面失效、存取访问控制错、算术操作溢出、数据格式非法、除数为零、非法指令、用户程序执行特权指令、分时系统中的时间片中断以及从用户态到核心态的切换等都是陷阱的例子。
    为了按中断源的轻重缓急处理响应中断,操作系统为不同的中断赋予不同的优先级。例如在UNIX系统中,外中断和陷阱的优先级共分为8级。为了禁止中断或屏蔽中断,CPU的处理器状态字PSW中也设有相应的优先级。如果中断源的优先级高于PSW的优先级,则CPU响应该中断源的请求;反之,CPU屏蔽该中断源的中断请求。
    各中断源的优先级在系统设计时给定,在系统运行时是固定的。而处理器的优先级则根据执行情况由系统程序动态设定。
    除了在优先级的设置方面有区别之外,中断和陷阱还有如下主要区别:
    陷阱通常由处理器正在执行的现行指令引起,而中断则是由与现行指令无关的中断源引起的。陷阱处理程序提供的服务为当前进程所用,而中断处理程序提供的服务则不是为了当前进程的。
    CPU执行完一条指令之后,下一条指令开始之前响应中断,而在一条指令执行中也可以响应陷阱。例如执行指令非法时,尽管被执行的非法指令不能执行结束,但CPU仍可对其进行处理。

  3. 软中断

    软中断的概念主要来源于UNIX系统。软中断是对应于硬中断而言的。通过硬件产生相应的中断请求,称为硬中断。而软中断则不然,它是在通信进程之间通过模拟硬中断而实现的一种通信方式。中断源发出软中断信号后,CPU或者接收进程在“适当的时机”进行中断处理或者完成软中断信号所对应的功能。这里“适当的时机”,表示接收软中断信号的进程须等到该接收进程得到处理器之后才能进行。如果该接收进程是占据处理器的,那么,该接收进程在接收到软中断信号后将立即转去执行该软中断信号所对应的功能。

  4. 中断处理过程
    一旦CPU响应中断,转人中断处理程序,系统就开始进行中断处理。下面对中断处理过程进行详细说明:

    1. CPU检查响应中断的条件是否满足。CPU响应中断的条件是:有来自于中断源的中断请求、CPU允许中断。如果中断响应条件不满足,则中断处理无法进行。

    2. 如果CPU响应中断,则CPU关中断,使其进入不可再次响应中断的状态。

    3. 保存被中断进程现场。为了在中断处理结束后能使进程正确地返回到中断点,系统必须保存当前处理器状态字PSW和程序计数器PC等的值。这些值一般保存在特定堆栈或硬件寄存器中。

    4. 分析中断原因,调用中断处理子程序。在多个中断请求同时发生时,处理优先级最高的中断源发出的中断请求。在系统中,为了处理上的方便,通常都是针对不同的中断源编制有不同的中断处理子程序(陷阱处理子程序)。这些子程序的人口地址(或陷阱指令的人口地址)存放在内存的特定单元中。再者,不同的中断源也对应着不同的处理器状态字PSW。这些不同的PSW被放在相应的内存单元中,与中断处理子程序人口地址一起构成中断向量。显然,根据中断或陷阱的种类,系统可由中断向量表迅速地找到该中断响应的优先级、中断处理子程序(或陷阱指令)的入口地址和对应的PSW。

    5. 执行中断处理子程序。对陷阱来说,在有些系统中则是通过陷阱指令向当前执行进程发出软中断信号后调用对应的处理子程序执行。

    6. 退出中断,恢复被中断进程的现场或调度新进程占据处理器。

    7. 开中断,CPU继续执行。

  5. 设备管理程序与中断方式

    处理器的高速和输入输出设备低速之间的矛盾,是设备管理要解决的一个重要问题。为了提高整体效率,减少在程序直接控制方式中的CPU等待时间以及提高系统的并行工作效率,采用中断方式来控制输入输出设备和内存与CPU之间的数据传送,是很有必要的。在硬件结构上,这种方式要求CPU与输入输出设备(或控制器)之间有相应的中断请求线,而且在输入输出设备控制器的控制状态寄存器上有相应的中断允许位。

信号

信号驱动的异步I/O是指一旦设备准备好,就主动通知应用程序,这种情况下应用程序就不需要查询设备状态。异步 I/O 和硬件上常提的中断的概念类似,信号是在软件层次上对中断机制的一种模拟。

  1. 信号通信机制
    软中断信号(signal,又简称为信号)是进程间通信机制中唯一的异步通信机制,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号到底什么时候到达。
    进程之间可以互相通过系统调用kill发送软中断信号,内核也可以因为内部事件而给进程发送信号,通知进程发生了某个事件。信号机制除了基本通知功能外,还可以传递附加信息。

  2. 处理信号
    收到信号的进程对各种信号有不同的处理方法,主要分为以下三类:

    • 类似中断的处理程序,对于需要处理的信号,进程可以指定处理函数,由该函数来处理。进程通过系统调用signal来指定进程对某个信号的处理行为。
    • 忽略某个信号,对该信号不做任何处理,就象未发生过一样。
    • 对该信号的处理保留系统的默认值,对大部分的信号的缺省操作是使得进程终止。

需要注意的是,信号处理函数注册后,当信号来临并被触发调用信号处理函数,当同样类型信号再次到来时,并不会执行信号处理函数,而是使用信号的系统默认处理方式,大部分都是使得进程终止。

  1. 信号可靠性
    从可靠性方面信号分为可靠信号与不可靠信号;信号的可靠与不可靠只与信号值有关,与信号的发送及安装函数无关。信号值位于 SIGRTMIN 和 SIGRTMAX 之间的信号都是可靠信号,可靠信号克服了信号可能丢失的问题。
    非实时信号都不支持排队,都是不可靠信号;实时信号都支持排队,都是可靠信号。
    不可靠信号就是指发送的信号内核不一定能够发送给 目标进程,信号可能丢失。 不可靠信号在内核中存储的方式是位图和链表,当内核接收一个信号后,先判断它是否 已经存在,不存在就把他对应的位置一,并将信号挂入链表,存在则丢弃信号。 可靠信号则是把信号放入队列,再链入链表,所以也就保证了信号不丢失。
    非可靠信号一般都有确定的用途及含义, 可靠信号则可以让用户自定义使用
    需要注意的是:这里的“实时”和实时操作系统中的“实时”没有任何联系,实时信号在处理速度上并不会比普通信号快,它们之间的区别就是:普通信号会对多次的同一个信号进行“合并”处理,而实时信号会一一处理。这就要求我们在编写信号监听函数时,要捕获普通信号,必须时刻轮训监听,因为系统默认会丢弃同种类型的普通信号!

  2. 信号传递顺序
    如果存在多个未决信号,同一个未决信号会按照发送顺序来递送信号,不同的未决信 号按照信号的序号大小来递送,序号小的信号会先被递送到进程。另外,linux中会优 先递送不可靠信号
    Linux中的信号机制优先级是:高优先级中断->低优先级中断->软中断->信号->进程运行。
    需要注意的是,在用户态不存在未决信号。信号处理一般发生在进程从内核态返回用户态的时候。内核空间没有信号处理机制,内核态也不会处理信号否者信号拥有系统最高权限,变得不再安全

  3. 多线程中信号造成死锁
    如果一个线程持有锁,在操作临界区内容时,被信号中断了,转而去执行信号处理函数, 而信号处理函数再次对临界区加锁就会造成死锁。
    解决的方法就是使用信号等待函数,线程阻塞等待信号处理函数直到处理完毕,也就是 所说的化异步为同步。

  4. 信号响应过程

    1. A进程调用信号发送函数,发送信号给B,这是软中断,所以A进程会进入内核态运行操作系统的信号调度代码
    2. 操作系统发现B进程正在运行,于是写入管理B进程的某个数据结构
    3. 操作系统返回给A,A继续执行
    4. B进程分配的处理器时间用完了,被时钟硬件中断
    5. 操作系统的时钟硬件中断处理函数准备挂起B进程,也就是把寄存器和函数堆栈保存起来,发现B进程收到了singal
    6. 操作系统在保存好B进程的stack和register后,新开stack(为了不干扰B进程真正的代码stack),激活B进程,B进程的信号处理函数。
  5. 信号生命周期(和响应过程类似)

    • 在目的进程中安装该信号。即设置捕获该信号时进程执行的操作,采用signal 或者 sigaction 系统调用来实现。
    • 信号被某个进程产生,同时设置该信号的目的进程(使用pid),之后交给操作系统进行管理。采用kill()、arise()、alarm()等系统调用来实现。
    • 信号在目的进程被注册。就是把信号值加入到进程的PCB(task_struct)中相关的数据结构里——未决信号的数据成员,信号携带的其他信息被保留到未决信的队列的某个sigqueue结构中。
    • 信号在进程中注销。在执行信号处理函数前,要把信号在进程中注销。
    • 信号生命的终结。进程终止当前的工作,保护上下文,执行信号处理函数,之后恢复。
  6. 信号阻塞集(屏蔽集、掩码)
    信号阻塞集也称信号屏蔽集、信号掩码。每个进程都有一个阻塞集,创建子进程时子进程将继承父进程的阻塞集。信号阻塞集用来描述哪些信号递送到该进程的时候被阻塞(在信号发生时记住它,直到进程准备好时再将信号通知进程)。
    所谓阻塞并不是禁止传送信号, 而是暂缓信号的传送。若将被阻塞的信号从信号阻塞集中删除,且对应的信号在被阻塞时发生了,进程将会收到相应的信号。

信号实现机制

  1. 发送信号
    内核给一个进程发送软中断信号的方法,是在进程所在的进程表项的信号域设置对应于该信号的位。如果信号发送给一个正在睡眠的进程,那么要看该进程进入睡眠的优先级,如果进程睡眠在可被中断的优先级上,则唤醒进程;否则仅设置进程表中信号域相应的位,而不唤醒进程。
    进程的 PCB 中有关于本进程中未决信号的数据成员 struct sigpending pending
    struct sigpending{    struct sigqueue *head, *tail;    sigset_t signal;};

第三个成员是进程中所有未决信号集,第一、第二个成员分别指向一个sigqueue类型的结构链(称之为”未决信号信息链”)的首尾,信息链中的每个sigqueue结构刻画一个特定信号所携带的信息,并指向下一个sigqueue结构:

struct sigqueue{    struct sigqueue *next;    siginfo_t info;}

信号在进程中注册指的就是信号值加入到进程的未决信号集sigset_t signal(每个信号占用一位)中,并且信号所携带的信息被保留到未决信号信息链的某个sigqueue结构中。只要信号在进程的未决信号集中,表明进程已经知道这些信号的存在,但还没来得及处理,或者该进程被信号阻塞。

当一个可靠信号发送给一个进程时,不管该信号是否已经在进程中注册,都会被再注册一次,因此,信号不会丢失。这意味着同一个可靠信号可以在同一个进程的未决信号信息链中占有多个sigqueue结构(进程每收到一个可靠信号,都会为它分配一个结构来注册该信号信息,并把该结构添加在未决信号链尾)。

当一个非可靠信号发送给一个进程时,如果该信号已经在进程中注册(通过sigset_t signal指示),则该信号将被丢弃,造成信号丢失。这意味着同一个非实时信号在进程的未决信号信息链中,至多占有一个sigqueue结构。

总之信号注册与否,与发送信号的函数(如kill()或sigqueue()等)以及信号安装函数(signal()及sigaction())无关,只与信号值有关(信号值小于SIGRTMIN的信号最多只注册一次,信号值在SIGRTMIN及SIGRTMAX之间的信号,只要被进程接收到就被注册)。

  1. 处理信号
    内核处理一个进程收到的信号的时机是在一个进程从内核态返回用户态时。所以,当一个进程在内核态下运行时,软中断信号并不立即起作用,要等到将返回用户态时才处理。进程只有处理完信号才会返回用户态,进程在用户态下不会有未处理完的信号。
    内核处理一个进程收到的软中断信号是在该进程的上下文中,因此,进程必须处于运行状态。当进程接收到一个它忽略的信号时,进程丢弃该信号,就像没有收到该信号似的继续运行。
    如果进程收到一个要捕捉的信号,那么进程从内核态返回用户态时执行用户定义的函数。而且执行用户定义的函数的方法很巧妙,内核在用户栈上创建一个新的层,该层中将返回地址的值设置成用户定义的处理函数的地址,这样进程从内核返回弹出栈顶时就返回到用户定义的函数处,从函数返回再弹出栈顶时,才返回原先进入内核的地方。这样做的原因是用户定义的处理函数不能且不允许在内核态下执行(如果用户定义的函数在内核态下运行的话,用户就可以获得任何权限)。
    对于非可靠信号来说,由于在未决信号信息链中最多只占用一个sigqueue结构,因此该结构被释放后,应该把信号在进程未决信号集中删除(信号注销完毕);而对于实时信号来说,可能在未决信号信息链中占用多个sigqueue结构,因此应该针对占用sigqueue结构的数目区别对待:如果只占用一个sigqueue结构(进程只收到该信号一次),则执行完相应的处理函数后应该把信号在进程的未决信号集中删除(信号注销完毕)。否则待该信号的所有sigqueue处理完毕后再在进程的未决信号集中删除该信号。
    当所有未被屏蔽的信号都处理完毕后,即可返回用户空间。对于被屏蔽的信号,当取消屏蔽后,在返回到用户空间时会再次执行上述检查处理的一套流程。

参考文献

CS_Offer/Signal.md at master · xuelangZF/CS_Offer · GitHub
信号处理的时机
面试中关于Linux的信号常问的问题 | 等英博客
linux系统编程之信号(一):中断与信号 - mickole - 博客园
Linux系统编程——进程间通信:信号中断处理 - CSDN博客

]]>
- - - - - <h3 id="什么是中断"><a href="#什么是中断" class="headerlink" title="什么是中断"></a>什么是中断</h3><ol> -<li><p>中断基本概念</p> -<blockquote> -<p>中断是指计算机在执行期间,系统内发生任何非寻 - - - - - - - - - -
- - - linux 安装指定版本MySql - - https://blog.genge.cc/2020/01/23/linux%20%E5%AE%89%E8%A3%85%E6%8C%87%E5%AE%9A%E7%89%88%E6%9C%ACMySql/ - 2020-01-23T07:12:39.000Z - 2020-01-23T07:12:39.000Z - - 由于工作环境、生产环境,我们使用的操作系统为为CentOS6.9,所需mysql版本为5.7,目前CentOS6.x系统默认mysql版本为5.1,这个版本是实在是太旧了。

彻底卸载系统已经安装的旧版本

  • 检查系统已经安装的mysql
    rpm -qa|grep -i mysql

  • 删除包
    rpm -ev mysql_lib_xxxx

  • 删除老版本安装残留文件
    find / -iname mysql* 删除对应目录已经文件

  • 删除my.cnf配置文件

使用yum安装MySql5.7

  • 下载mysql5.7源
    wget dev.mysql.com/get/mysql-community-release-el6-5.noarch.rpm

  • 安装源
    yum localinstall mysql-community-release-el6-5.noarch.rpm

  • 查看可用源中包含哪些版本并开启指定版本

    yum repolist all | grep mysqlyum-config-manager --disable mysql56-communityyum-config-manager --disable mysql55-communityyum-config-manager --enable mysql57-community
  • yum安装mysql
    yum install mysql-community-server

  • 启动mysql
    service mysqld start

  • 设置开机自动启动

chkconfig --list | grep mysqldchkconfig mysqld on
  • 安装设置命令
    mysql_secure_installation

修改root密码

因为刚才启动的时候是系统默认配置的临时密码
使用如下命令可以查看,并且修改:

sudo grep 'temporary password' /var/log/mysqld.logmysql -u root -p ALTER USER 'root'@'localhost' IDENTIFIED BY 'newPassword';

设置允许连接数据库

命令如下:

mysql -u root -p grant all privileges on *.* to root@"%" identified by 'passwordith grant option;  flush privileges;

遇到的问题

  • 比较奇怪,域名解析错误
    在yum install 的时候,发现大量的[Errno 14] PYCURL ERROR 6 - “Couldn’t resolve host ‘mirrors.aliyun.com’”错误,开始以为自己源设置错误,后来才知道,机器卡死过一次,导致系统莫名其妙的错误,了解网络的很快就知道需要设置系统的DNS
    /etc/resolv.conf文件中加入如下内容:
    nameserver 8.8.8.8 nameserver 114.114.114.114
    之前是啥也没有的,都是些没用的注释解释信息
]]>
- - - - - <p>由于工作环境、生产环境,我们使用的操作系统为为CentOS6.9,所需mysql版本为5.7,目前CentOS6.x系统默认mysql版本为5.1,这个版本是实在是太旧了。</p> -<h3 id="彻底卸载系统已经安装的旧版本"><a href="#彻底卸载系统已经安装的旧 - - - - - - - - - -
- - - TCP 三次握手和四次挥手 - - https://blog.genge.cc/2020/01/23/TCP%20%E4%B8%89%E6%AC%A1%E6%8F%A1%E6%89%8B%E5%92%8C%E5%9B%9B%E6%AC%A1%E6%8C%A5%E6%89%8B/ - 2020-01-23T00:12:45.000Z - 2020-01-23T00:12:45.000Z - - 首先需要了解的知识
  • TCP的包是没有IP地址的,那是IP层上的事。但是有源端口和目标端口。
  • 一个TCP连接需要四个元组来表示是同一个连接(src_ip, src_port, dst_ip, dst_port)准确说是五元组,还有一个是协议。但因为这里只是说TCP协议,所以,这里我只说四元组。
  • 注意上图中的四个非常重要的东西:
    • Sequence Number是包的序号,用来解决网络包乱序(reordering)问题。(seq)
    • Acknowledgement Number就是ACK——用于确认收到,用来解决不丢包的问题。
    • Window又叫Advertised-Window,也就是著名的滑动窗口(Sliding Window),用于解决流控的。
    • TCP Flag ,也就是包的类型,主要是用于操控TCP的状态机的。主要有SYN、FIN、ACK等

TCP的状态机

其实,网络上的传输是没有连接的,包括TCP也是一样的。而TCP所谓的“连接”,其实只不过是在通讯的双方维护一个“连接状态”,让它看上去好像有连接一样。所以,TCP的状态变换是非常重要的。
如图:(用excel画图的难受,又早不到其他好工具,能推荐个吗?)
TCP连接、通讯、断开流程图
图中Client 、Server并不是严格区分的,TCP是全双工的,双方都可以充当Server、Client

  1. 三次握手

    1. 第一次握手:建立连接。客户端发送连接请求报文段,将SYN位置为1,Sequence Number为x;然后,客户端进入SYN_SEND状态,等待服务器的确认;
    2. 第二次握手:服务器收到SYN报文段。服务器收到客户端的SYN报文段,需要对这个SYN报文段进行确认,设置Acknowledgment Number为x+1(Sequence Number+1);同时,自己自己还要发送SYN请求信息,将SYN位置为1,Sequence Number为y;服务器端将上述所有信息放到一个报文段(即SYN+ACK报文段)中,一并发送给客户端,此时服务器进入SYN_RECV状态;
    3. 第三次握手:客户端收到服务器的SYN+ACK报文段。然后将Acknowledgment Number设置为y+1,向服务器发送ACK报文段,这个报文段发送完毕以后,客户端和服务器端都进入ESTABLISHED状态,完成TCP三次握手。
      完成了三次握手,客户端和服务器端就可以开始传送数据。以上就是TCP三次握手的总体介绍。
  2. 四次挥手
    当客户端和服务器通过三次握手建立了TCP连接以后,当数据传送完毕,肯定是要断开TCP连接的啊。那对于TCP的断开连接,这里就有了神秘的“四次分手”。

    1. 第一次分手:主机1(可以使客户端,也可以是服务器端),设置Sequence Number和Acknowledgment Number,向主机2发送一个FIN报文段;此时,主机1进入FIN_WAIT_1状态;这表示主机1(client)没有数据要发送给主机2(server)了
    2. 第二次分手:主机2收到了主机1发送的FIN报文段,向主机1回一个ACK报文段,Acknowledgment Number为Sequence Number加1;主机1进入FIN_WAIT_2状态;主机2告诉主机1,我“同意”你的关闭请求;
    3. 第三次分手:主机2向主机1发送FIN报文段,请求关闭连接,同时主机2进入LAST_ACK状态;
    4. 第四次分手:主机1收到主机2发送的FIN报文段,向主机2发送ACK报文段,然后主机1进入TIME_WAIT状态;主机2收到主机1的ACK报文段以后,就关闭连接;此时,主机1等待2MSL后依然没有收到回复,则证明Server端已正常关闭,那好,主机1也可以关闭连接了。

至此,TCP的四次分手就这么愉快的完成了。当你看到这里,你的脑子里会有很多的疑问,很多的不懂,感觉很凌乱;没事,我们继续总结。

  1. 三次握手和四次挥手意义
    对于建链接的3次握手,主要是要初始化Sequence Number 的初始值。通信的双方要互相通知对方自己的初始化的Sequence Number(缩写为ISN:Inital Sequence Number)——所以叫SYN,全称Synchronize Sequence Numbers。也就上图中的 x 和 y。这个号要作为以后的数据通信的序号,以保证应用层接收到的数据不会因为网络上的传输的问题而乱序(TCP会用这个序号来拼接数据)。
    对于四次挥手,其实仔细看是2次,因为TCP是全双工的,所以,发送方和接收方都需要Fin和Ack。只不过,有一方是被动的,所以看上去就成了所谓的4次挥手。如果两边同时断连接,那就会就进入到CLOSING状态,然后到达TIME_WAIT状态。

为什么要三次握手

在谢希仁著《计算机网络》第四版中讲“三次握手”的目的是“为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误”。在另一部经典的《计算机网络》一书中讲“三次握手”的目的是为了解决“网络中存在延迟的重复分组”的问题。

在谢希仁著《计算机网络》书中同时举了一个例子,如下:

“已失效的连接请求报文段”的产生在这样一种情况下:client发出的第一个连接请求报文段并没有丢失,而是在某个网络结点长时间的滞留了,以致延误到连接释放以后的某个时间才到达server。本来这是一个早已失效的报文段。但server收到此失效的连接请求报文段后,就误认为是client再次发出的一个新的连接请求。于是就向client发出确认报文段,同意建立连接。假设不采用“三次握手”,那么只要server发出确认,新的连接就建立了。由于现在client并没有发出建立连接的请求,因此不会理睬server的确认,也不会向server发送数据。但server却以为新的运输连接已经建立,并一直等待client发来数据。这样,server的很多资源就白白浪费掉了。采用“三次握手”的办法可以防止上述现象发生。例如刚才那种情况,client不会向server的确认发出确认。server由于收不到确认,就知道client并没有要求建立连接。”
计算机网络三次握手

一句话总结就是:防止了服务器端的一直等待而浪费资源。

为什么要四次挥手
TCP协议是一种面向连接的、可靠的、基于字节流的运输层通信协议。TCP是全双工模式,这就意味着,当主机1发出FIN报文段时,只是表示主机1已经没有数据要发送了,主机1告诉主机2,它的数据已经全部发送完毕了;但是,这个时候主机1还是可以接受来自主机2的数据;当主机2返回ACK报文段时,表示它已经知道主机1没有数据发送了,但是主机2还是可以发送数据到主机1的;当主机2也发送了FIN报文段时,这个时候就表示主机2也没有数据要发送了,就会告诉主机1,我也没有数据要发送了,之后彼此就会愉快的中断这次TCP连接。

为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSED状态?
虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSED状态了,但是我们必须假象网络是不可靠的,有可能最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。

状态机状态解释

  • CLOSED: 这个没什么好说的了,表示初始状态,也是最后状态。
  • LISTEN: 这个也是非常容易理解的一个状态,表示服务器端的某个SOCKET处于监听状态,可以接受连接了。
  • SYN_RCVD: 这个状态表示接受到了SYN报文,在正常情况下,这个状态是服务器端的SOCKET在建立TCP连接时的三次握手会话过程中的一个中间状态,很短暂,基本上用netstat你是很难看到这种状态的,除非你特意写了一个客户端测试程序,故意将三次TCP握手过程中最后一个ACK报文不予发送。因此这种状态时,当收到客户端的ACK报文后,它会进入到ESTABLISHED状态。
  • SYN_SENT: 这个状态与SYN_RCVD遥想呼应,当客户端SOCKET执行CONNECT连接时,它首先发送SYN报文,因此也随即它会进入到了SYN_SENT状态,并等待服务端的发送三次握手中的第2个报文。SYN_SENT状态表示客户端已发送SYN报文。
  • ESTABLISHED:这个容易理解了,表示连接已经建立了。
  • FIN_WAIT_1: 这个状态要好好解释一下,其实FIN_WAIT_1和FIN_WAIT_2状态的真正含义都是表示等待对方的FIN报文。而这两种状态的区别是:FIN_WAIT_1状态实际上是当SOCKET在ESTABLISHED状态时,它想主动关闭连接,向对方发送了FIN报文,此时该SOCKET即进入到FIN_WAIT_1状态。而当对方回应ACK报文后,则进入到FIN_WAIT_2状态,当然在实际的正常情况下,无论对方何种情况下,都应该马上回应ACK报文,所以FIN_WAIT_1状态一般是比较难见到的,而FIN_WAIT_2状态还有时常常可以用netstat看到。
  • FIN_WAIT_2:上面已经详细解释了这种状态,实际上FIN_WAIT_2状态下的SOCKET,表示半连接,也即有一方要求close连接,但另外还告诉对方,我暂时还有点数据需要传送给你,稍后再关闭连接。
  • TIME_WAIT: 表示收到了对方的FIN报文,并发送出了ACK报文,就等2MSL后即可回到CLOSED可用状态了。如果FIN_WAIT_1状态下,收到了对方同时带FIN标志和ACK标志的报文时,可以直接进入到TIME_WAIT状态,而无须经过FIN_WAIT_2状态。
  • CLOSING: 这种状态比较特殊,实际情况中应该是很少见,属于一种比较罕见的例外状态。正常情况下,当你发送FIN报文后,按理来说是应该先收到(或同时收到)对方的ACK报文,再收到对方的FIN报文。但是CLOSING状态表示你发送FIN报文后,并没有收到对方的ACK报文,反而却也收到了对方的FIN报文。什么情况下会出现此种情况呢?其实细想一下,也不难得出结论:那就是如果双方几乎在同时close一个SOCKET的话,那么就出现了双方同时发送FIN报文的情况,也即会出现CLOSING状态,表示双方都正在关闭SOCKET连接。
  • CLOSE_WAIT: 这种状态的含义其实是表示在等待关闭。怎么理解呢?当对方close一个SOCKET后发送FIN报文给自己,你系统毫无疑问地会回应一个ACK报文给对方,此时则进入到CLOSE_WAIT状态。接下来呢,实际上你真正需要考虑的事情是察看你是否还有数据发送给对方,如果没有的话,那么你也就可以close这个SOCKET,发送FIN报文给对方,也即关闭连接。所以你在CLOSE_WAIT状态下,需要完成的事情是等待你去关闭连接。
  • LAST_ACK: 这个状态还是比较容易好理解的,它是被动关闭一方在发送FIN报文后,最后等待对方的ACK报文。当收到ACK报文后,也即可以进入到CLOSED可用状态了。

参考文献

TCP 的那些事儿(上) | | 酷 壳 - CoolShell
通俗大白话来理解TCP协议的三次握手和四次分手 · Issue #14 · jawil/blog · GitHub
面试常考题-TCP三次握手与四次握手 - CSDN博客
TCP协议中的三次握手和四次挥手(图解) - CSDN博客
Linux Socket 网络编程

]]>
- - - - - <h3 id="首先需要了解的知识"><a href="#首先需要了解的知识" class="headerlink" title="首先需要了解的知识"></a>首先需要了解的知识</h3><ul> -<li>TCP的包是没有IP地址的,那是IP层上的事。但是有源端口和目标端口。< - - - - - - - - - -
- - - 网易互娱面试总结 - - https://blog.genge.cc/2020/01/23/%E7%BD%91%E6%98%93%E4%BA%92%E5%A8%B1%E9%9D%A2%E8%AF%95%E6%80%BB%E7%BB%93/ - 2020-01-23T00:12:45.000Z - 2020-01-23T00:12:45.000Z - - 现场二面技术总监面试

这个人看起来挺和善的,实际上还是有一套的,让你写代码,然后眼睛编译给你指出错误,然后修改,然后他就去玩手机了…

  1. linux env multi-thread gdb debug 多线程调试 (回答对一半)
    首先介绍下基本命令
    • info threads 显示当前可以调试的所有线程,gdb会为每一个线程分配一个唯一ID,利用这个唯一Id可以切换到这个线程上下文环境中,并且前面有*标识的是当前调试的线程
    • thread ID 切换调试的线程为指定ID的线程。这个Id是gdb为每个线程分配的,并不是操作系统分配的PID,可以通过info threads命令查看
    • break xx.cpp:123 thread all 在所有线程中相应的行上设置断点
    • break apply ID1 ID2 cmd 让一个或者多个线程执行gdb命令cmd
    • break apply all cmd 让所有被调试线程执行GDB命令command
    • set print thread-events 设置线程创建提醒 当运行过程总产生新线程的时候会打印
    • set scheduler-locking off|on|step 这个是重点,经常被问到,在使用step或者continue命令调试当前被调试线程的时候,其他线程也是同时执行的,怎么只让被调试程序执行呢?通过这个命令就可以实现这个需求。
      • off 不锁定任何线程,也就是所有线程都执行,这是默认值。
      • on 只有当前被调试程序会执行。.
      • step 在单步的时候,除了next过一个函数的情况(熟悉情况的人可能知道,这其实是一个设置断点然后continue的行为)以外,只有当前线程会执行。该模式是对single-stepping模式的优化。此模式会阻止其他线程在当前线程单步调试时,抢占当前线。因此调试的焦点不会被以外的改变。其他线程不可能抢占当前的调试线程。其他线程只有下列情况下会重新获得运行的机会:当你‘next’一个函数调用的时候。当你使用诸如‘continue’、‘until‘、’finish‘命令的时候。其他线程遇到设置好的断点的时候。
  • 调试C++或者C的宏
    在编译程序的时候,加上-ggdb3参数,这样就可以调试宏
    • info macro – 你可以查看这个宏在哪些文件里被引用了,以及宏定义是什么样的。
    • macro – 你可以查看宏展开的样子。
  • 关联源文件
    • 如果在编译情况下加上-g参数,那么就可以包含debug信息,否者gdb找不到符号表
    • 可以使用directory命令来设置源文件的目录
  • 条件断点
    基本语法 break [where] if [condition] 尤其是在一个循环或递归中,或是要监视某个变量。注意,这个设置是在GDB中的,只不过每经过那个断点时GDB会帮你检查一下条件是否满足。
  • 添加参数
    1. gdb命令行的 –args 参数
    2. gdb环境中 set args命令。
  • 设置变量
    1. 可以直接使用set命令 设置上下文环境变量值,可以模拟一些很难在测试中出现的情况,以防未来程序出错
    2. 声明变量,然后使用,语法为$name = 1
  • X命令
    平时我们一般使用p命令打印参数值,但是这个命令必须指定变量名,不知道变量名的时候,我们可以使用X命令
    1. x\x 以十六进制输出
    2. x\d 以十进制输出
    3. x\c 以单字符输出
    4. x\i 反汇编 – 通常,我们会使用 x\10i $ip-20 来查看当前的汇编($ip是指令寄存器)
    5. x\s 以字符串输出
  • command命令 把一组命令录制下来打包成‘宏’
  1. 循环队列判空 (OK)
    有三种方式处理这种问题

    1. 队列Queue结构中保存一个计数器count表示当前队列元素个数(最简单粗暴),但count等于队列cap的时候就队列满,count为0的时候队列空
    2. 少用一个元素空间,约定以“队列头指针front在队尾指针rear的下一个位置上”作为队列“满”状态的标志。这种方法比较常用,但是面试官不让用…. front指向队首元素,rear指向队尾元素的下一个元素。即:
      • 队空时: front=rear
      • 队满时: (rear+1)%maxsize=front
    3. 还有一个比较取巧的办法,优化第一种方案:使用一个状态flag变量,初始值为0,但入队成功置flag = 1,当出队成功设置flag = 0。我们可以使用 front == rear && flag 表示队列满(在入队操作之后导致front=rear),可以使用front == rear && !flag表示队列空(出队后导致f==r,显然是队列空)
  2. 单向队列反转 (OK) 很简单

struct Node{    int data;    Node * next;};Node* reverse_list(Node* head){    if(head == nullptr) return head;    Node* node = nullptr;    node = head;    head = head->next;    node = nullptr;    while(head != nullptr)    {        Node* next = head->next;        head->next = node;        node = head;        head = next;    }    return node;}

现场面一面回忆总结

估计是小组长之类的面试官吧,去之前我还特意看下自己的衣装是否整洁,这个面试官感觉是从工地上回来的,衣服上很脏,典型程序员面孔,他问的问题算是比较全面
操作系统(linux)、数据库(mysql)、算法、数据结构、计算机网络基础、网络编程、语言基础(C++语言)、并发、以及具体业务设计,还有项目基本介绍,游戏服务器架构简单介绍

常见模块实现

  1. 定时器实现方式目前应用比较多的有时间轮和最小堆方式 , 优缺点其实就是算法复杂度:
    实现方式StartTimerStopTimerPerTickBookkeeping
    基于链表 O(1)O(n)O(n)
    基于排序链表O(n)O(1)O(1)
    基于最小堆O(lgn)O(1)O(1)
    基于时间轮O(1)O(1)O(1)
    https:\www.ibm.com\developerworks\cn\linux\l-cn-timers\

  2. 斐波那契数 多种实现

    1. 递归 最简单 粗暴 效率最低 存在大量重复计算
    2. 循环叠加 算法复杂度为O(n)
    3. 申请额外数组保存结果 去除重复计算 空间换时间
    4. 利用数学公式推导,矩阵相乘推导公式,算法复杂度为O(logn) 效率最高
      {f(n), f(n-1), f(n-1), f(n-2)} ={1, 1, 1,0}n-1
      {f(n), f(n-1), f(n-1), f(n-2)}
      详情见 https:\www.cnblogs.com\python27\archive\2011\11\25\2261980.html
    5. 通项公式 这个实在是牛逼 一个公式搞定… 不是数学系 这些方法确实想不出 只能记忆
        int Fibonacci(int n){    double tmp=sqrt(double(5));    return int((pow((1+tmp)\2,n)-pow((1-tmp)\2,n))\tmp);}
    6. 定理
        int Fibonacci(int n){    if(n==0)return 0;    if(n==1||n==2)return 1;    \\当n>=3时,n>n\2+1    int x=Fibonacci(n\2);    int y=Fibonacci(n\2+1);    if(n&1)return x*x+y*y;    return x*(2*y-x);}
  3. 敏感字过滤算法

    1. 正则匹配 面试官一般不会让你用这个 因为要匹配的内容太多 写正则表达式就很烦… 效率还不高 KMP算法 太慢 不能用
    2. 自己当时想到的一张方法为字典树TrieTree 这个方法太耗空间 时间复杂度为O(key_max_len) 很多生产环境确实是用这个实现的,别名有限状态机 DFA:DFA即Deterministic Finite Automaton,也就是确定有穷自动机
    3. 其他什么优化算法 其实也不用
  4. 数据库

    1. 数据库事务特性
      ACID 原子性 一致性 隔离性 持久性dura

    2. 事务隔离级别

        隔离级别               脏读(Dirty Read)          不可重复读(NonRepeatable Read)     幻读(Phantom Read) 

      未提交读(Read uncommitted) 可能 可能 可能

      已提交读(Read committed) 不可能 可能 可能

      可重复读(Repeatable read) 不可能 不可能 可能

      可串行化(Serializable ) 不可能 不可能 不可能

      ·未提交读(Read Uncommitted):允许脏读,也就是可能读取到其他会话中未提交事务修改的数据(事务之间关系)
      ·提交读(Read Committed):只能读取到已经提交的数据。Oracle等多数数据库默认都是该级别 (不重复读) (事务之间关系)
      ·可重复读(Repeated Read):可重复读。在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。在SQL标准中,该隔离级别消除了不可重复读,但是还存在幻象读 (事务内部)
      ·串行读(Serializable):完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞
      可重复读和提交读是矛盾的。在同一个事务里,如果保证了可重复读,就会看不到其他事务的提交,违背了提交读;如果保证了提交读,就会导致前后两次读到的结果不一致,违背了可重复读。
      可以这么讲,InnoDB提供了这样的机制,在默认的可重复读的隔离级别里,可以使用加锁读去查询最新的数据(提交读)。
      MySQL InnoDB的可重复读并不保证避免幻读,需要应用使用加锁读来保证。而这个加锁度使用到的机制就是next-key locks。
      总结:MySQL 四种事务隔离级的说明 - jyzhou - 博客园
      四个级别逐渐增强,每个级别解决一个问题。事务级别越高,性能越差,大多数环境read committed 可以用.记住4个隔离级别的特点(上面的例子);http:\\www.cnblogs.com\zhoujinyi\p\3437475.html

    3. 事务实现原理
      https:\draveness.me\mysql-transaction 介绍事务ACID的火滚日志实现 https:\www.cnblogs.com\wy123\p\8365234.html 具体日志格式

    4. innodb和myisam存储引擎的区别 https:\blog.csdn.net\xifeijian\article\details\20316775

      • MyISAM类型不支持事务处理等高级处理,而InnoDB类型支持,MyISAM类型的表强调的是性能,其执行数度比InnoDB类型更快,但是不提供事务支持,而InnoDB提供事务支持以及外部键等高级数据库功能。
      • InnoDB不支持FULLTEXT类型的索引。
      • InnoDB 中不保存表的具体行数,也就是说,执行select count() from table时,InnoDB要扫描一遍整个表来计算有多少行,但是MyISAM只要简单的读出保存好的行数即可。注意的是,当count()语句包含 where条件时,两种表的操作是一样的。
      • 对于AUTO_INCREMENT类型的字段,InnoDB中必须包含只有该字段的索引,但是在MyISAM表中,可以和其他字段一起建立联合索引。
      • 细节可以看链接 https:\blog.csdn.net\xifeijian\article\details\20316775和下面一个介绍
      • https:\www.jianshu.com\p\a957b18ba40d
    5. 对于like查询啥时候会用到索引 http:\thephper.com?p=142

      like 不能用索引? 这个确实不知道 难受
      尽量减少like,但不是绝对不可用,”xxxx%” 是可以用到索引的,
      想象一下,你在看一本成语词典,目录是按成语拼音顺序建立,查询需求是,你想找以 “一”字开头的成语(”一%“),和你想找包含一字的成语(“%一%”)
      除了like,以下操作符也可用到索引:
      <,<=,=,>,>=,BETWEEN,IN
      <>,not in ,!=则不行

    6. 索引类型
      https:\segmentfault.com\q\1010000003832312 http:\blog.codinglabs.org\articles\theory-of-mysql-index.html

]]>
- - - - - <h2 id="现场二面技术总监面试"><a href="#现场二面技术总监面试" class="headerlink" title="现场二面技术总监面试"></a>现场二面技术总监面试</h2><p>这个人看起来挺和善的,实际上还是有一套的,让你写代码,然后眼睛编译给你指出错 - - - - - - - -
- - - linux 内核RingBuffer实现 - - https://blog.genge.cc/2020/01/22/linux%20%E5%86%85%E6%A0%B8RingBuffer%E5%AE%9E%E7%8E%B0/ - 2020-01-22T07:12:45.000Z - 2020-01-22T07:12:45.000Z - - 实现方式非常巧妙,刚开始看的有点奇怪,当发现实现原理后惊讶了一番..

/**@brief 仿照linux kfifo写的ring buffer* ring_buffer.h * */ #ifndef KFIFO_HEADER_H #define KFIFO_HEADER_H #include <inttypes.h>#include <string.h>#include <stdlib.h>#include <stdio.h>#include <errno.h>#include <assert.h> //判断x是否是2的次方#define is_power_of_2(x) ((x) != 0 && (((x) & ((x) - 1)) == 0))//取a和b中最小值#define min(a, b) (((a) < (b)) ? (a) : (b)) struct ring_buffer{    void         *buffer;     //缓冲区    uint32_t     size;       //大小    uint32_t     in;         //入口位置    uint32_t       out;        //出口位置    pthread_mutex_t *f_lock;    //互斥锁};//初始化缓冲区struct ring_buffer* ring_buffer_init(void *buffer, uint32_t size, pthread_mutex_t *f_lock){    assert(buffer);    struct ring_buffer *ring_buf = NULL;    if (!is_power_of_2(size))    {    fprintf(stderr,"size must be power of 2.\n");        return ring_buf;    }    ring_buf = (struct ring_buffer *)malloc(sizeof(struct ring_buffer));    if (!ring_buf)    {        fprintf(stderr,"Failed to malloc memory,errno:%u,reason:%s",            errno, strerror(errno));        return ring_buf;    }    memset(ring_buf, 0, sizeof(struct ring_buffer));    ring_buf->buffer = buffer;    ring_buf->size = size;    ring_buf->in = 0;    ring_buf->out = 0;        ring_buf->f_lock = f_lock;    return ring_buf;}//释放缓冲区void ring_buffer_free(struct ring_buffer *ring_buf){    if (ring_buf)    {    if (ring_buf->buffer)    {        free(ring_buf->buffer);        ring_buf->buffer = NULL;    }    free(ring_buf);    ring_buf = NULL;    }} //缓冲区的长度uint32_t __ring_buffer_len(const struct ring_buffer *ring_buf){    return (ring_buf->in - ring_buf->out);} //从缓冲区中取数据uint32_t __ring_buffer_get(struct ring_buffer *ring_buf, void * buffer, uint32_t size){    assert(ring_buf || buffer);    uint32_t len = 0;    size  = min(size, ring_buf->in - ring_buf->out);            /* first get the data from fifo->out until the end of the buffer */    len = min(size, ring_buf->size - (ring_buf->out & (ring_buf->size - 1)));    memcpy(buffer, ring_buf->buffer + (ring_buf->out & (ring_buf->size - 1)), len);    /* then get the rest (if any) from the beginning of the buffer */    memcpy(buffer + len, ring_buf->buffer, size - len);    ring_buf->out += size;    return size;}//向缓冲区中存放数据uint32_t __ring_buffer_put(struct ring_buffer *ring_buf, void *buffer, uint32_t size){    assert(ring_buf || buffer);    uint32_t len = 0;    size = min(size, ring_buf->size - ring_buf->in + ring_buf->out);    /* first put the data starting from fifo->in to buffer end */    len  = min(size, ring_buf->size - (ring_buf->in & (ring_buf->size - 1)));    memcpy(ring_buf->buffer + (ring_buf->in & (ring_buf->size - 1)), buffer, len);    /* then put the rest (if any) at the beginning of the buffer */    memcpy(ring_buf->buffer, buffer + len, size - len);    ring_buf->in += size;    return size;} uint32_t ring_buffer_len(const struct ring_buffer *ring_buf){    uint32_t len = 0;    pthread_mutex_lock(ring_buf->f_lock);    len = __ring_buffer_len(ring_buf);    pthread_mutex_unlock(ring_buf->f_lock);    return len;} uint32_t ring_buffer_get(struct ring_buffer *ring_buf, void *buffer, uint32_t size){    uint32_t ret;    pthread_mutex_lock(ring_buf->f_lock);    ret = __ring_buffer_get(ring_buf, buffer, size);    //buffer中没有数据    if (ring_buf->in == ring_buf->out)    ring_buf->in = ring_buf->out = 0;    pthread_mutex_unlock(ring_buf->f_lock);    return ret;} uint32_t ring_buffer_put(struct ring_buffer *ring_buf, void *buffer, uint32_t size){    uint32_t ret;    pthread_mutex_lock(ring_buf->f_lock);    ret = __ring_buffer_put(ring_buf, buffer, size);    pthread_mutex_unlock(ring_buf->f_lock);    return ret;}#endif

使用栗子,采用多线程模拟生产者和消费者编写测试程序,如下所示:

/**@brief ring buffer测试程序,创建两个线程,一个生产者,一个消费者。 * 生产者每隔1秒向buffer中投入数据,消费者每隔2秒去取数据。 * */#include "ring_buffer.h"#include <pthread.h>#include <time.h> #define BUFFER_SIZE  1024 * 1024 typedef struct student_info{    uint64_t stu_id;    uint32_t age;    uint32_t score;}student_info;  void print_student_info(const student_info *stu_info){    assert(stu_info);    printf("id:%lu\t",stu_info->stu_id);    printf("age:%u\t",stu_info->age);    printf("score:%u\n",stu_info->score);} student_info * get_student_info(time_t timer){    student_info *stu_info = (student_info *)malloc(sizeof(student_info));    if (!stu_info)    {    fprintf(stderr, "Failed to malloc memory.\n");    return NULL;    }    srand(timer);    stu_info->stu_id = 10000 + rand() % 9999;    stu_info->age = rand() % 30;    stu_info->score = rand() % 101;    print_student_info(stu_info);    return stu_info;} void * consumer_proc(void *arg){    struct ring_buffer *ring_buf = (struct ring_buffer *)arg;    student_info stu_info;     while(1)    {    sleep(2);    printf("------------------------------------------\n");    printf("get a student info from ring buffer.\n");    ring_buffer_get(ring_buf, (void *)&stu_info, sizeof(student_info));    printf("ring buffer length: %u\n", ring_buffer_len(ring_buf));    print_student_info(&stu_info);    printf("------------------------------------------\n");    }    return (void *)ring_buf;} void * producer_proc(void *arg){    time_t cur_time;    struct ring_buffer *ring_buf = (struct ring_buffer *)arg;    while(1)    {    time(&cur_time);    srand(cur_time);    int seed = rand() % 11111;    printf("******************************************\n");    student_info *stu_info = get_student_info(cur_time + seed);    printf("put a student info to ring buffer.\n");    ring_buffer_put(ring_buf, (void *)stu_info, sizeof(student_info));    printf("ring buffer length: %u\n", ring_buffer_len(ring_buf));    printf("******************************************\n");    sleep(1);    }    return (void *)ring_buf;} int consumer_thread(void *arg){    int err;    pthread_t tid;    err = pthread_create(&tid, NULL, consumer_proc, arg);    if (err != 0)    {    fprintf(stderr, "Failed to create consumer thread.errno:%u, reason:%s\n",        errno, strerror(errno));    return -1;    }    return tid;}int producer_thread(void *arg){    int err;    pthread_t tid;    err = pthread_create(&tid, NULL, producer_proc, arg);    if (err != 0)    {    fprintf(stderr, "Failed to create consumer thread.errno:%u, reason:%s\n",        errno, strerror(errno));    return -1;    }    return tid;}  int main(){    void * buffer = NULL;    uint32_t size = 0;    struct ring_buffer *ring_buf = NULL;    pthread_t consume_pid, produce_pid;     pthread_mutex_t *f_lock = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t));    if (pthread_mutex_init(f_lock, NULL) != 0)    {    fprintf(stderr, "Failed init mutex,errno:%u,reason:%s\n",        errno, strerror(errno));    return -1;    }    buffer = (void *)malloc(BUFFER_SIZE);    if (!buffer)    {    fprintf(stderr, "Failed to malloc memory.\n");    return -1;    }    size = BUFFER_SIZE;    ring_buf = ring_buffer_init(buffer, size, f_lock);    if (!ring_buf)    {    fprintf(stderr, "Failed to init ring buffer.\n");    return -1;    }#if 0    student_info *stu_info = get_student_info(638946124);    ring_buffer_put(ring_buf, (void *)stu_info, sizeof(student_info));    stu_info = get_student_info(976686464);    ring_buffer_put(ring_buf, (void *)stu_info, sizeof(student_info));    ring_buffer_get(ring_buf, (void *)stu_info, sizeof(student_info));    print_student_info(stu_info);#endif    printf("multi thread test.......\n");    produce_pid  = producer_thread((void*)ring_buf);    consume_pid  = consumer_thread((void*)ring_buf);    pthread_join(produce_pid, NULL);    pthread_join(consume_pid, NULL);    ring_buffer_free(ring_buf);    free(f_lock);    return 0;}

参考文献

linux内核数据结构之kfifo - Daleshi的技术随笔 - 博客园

]]>
- - - - - <p>实现方式非常巧妙,刚开始看的有点奇怪,当发现实现原理后惊讶了一番..</p> -<pre><code class="hljs c++"><span class="hljs-comment">/**@brief 仿照linux kfifo写的ring buffer</span> - - - - - - - - - -
- -
diff --git a/categories/C-C/index.html b/categories/C-C/index.html deleted file mode 100644 index 1a9a8da..0000000 --- a/categories/C-C/index.html +++ /dev/null @@ -1,173 +0,0 @@ - - - - - - - -Genge 随笔 - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - - - -
- - -
- - - -
-
- - \ No newline at end of file diff --git a/categories/Game/index.html b/categories/Game/index.html deleted file mode 100644 index 7152e8f..0000000 --- a/categories/Game/index.html +++ /dev/null @@ -1,140 +0,0 @@ - - - - - - - -Genge 随笔 - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - -
-
- - -

2020

- - - -
- -
- 2020-01-23 -
各类APP排行榜实现
-
-
-
- - - -
-
- -
- - -
- - - -
-
- - \ No newline at end of file diff --git a/categories/Linux/index.html b/categories/Linux/index.html deleted file mode 100644 index a45895e..0000000 --- a/categories/Linux/index.html +++ /dev/null @@ -1,151 +0,0 @@ - - - - - - - -Genge 随笔 - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - - - -
- - -
- - - -
-
- - \ No newline at end of file diff --git a/categories/Mysql/index.html b/categories/Mysql/index.html deleted file mode 100644 index f20548c..0000000 --- a/categories/Mysql/index.html +++ /dev/null @@ -1,140 +0,0 @@ - - - - - - - -Genge 随笔 - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - - - -
- - -
- - - -
-
- - \ No newline at end of file diff --git a/categories/Redis/index.html b/categories/Redis/index.html deleted file mode 100644 index 0557403..0000000 --- a/categories/Redis/index.html +++ /dev/null @@ -1,140 +0,0 @@ - - - - - - - -Genge 随笔 - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - -
-
- - -

2020

- - - -
- -
- 2020-07-10 -
Redis Guide 使用手册
-
-
-
- - - -
-
- -
- - -
- - - -
-
- - \ No newline at end of file diff --git a/categories/Shell/index.html b/categories/Shell/index.html deleted file mode 100644 index 18e5a9d..0000000 --- a/categories/Shell/index.html +++ /dev/null @@ -1,140 +0,0 @@ - - - - - - - -Genge 随笔 - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - - - -
- - -
- - - -
-
- - \ No newline at end of file diff --git a/categories/emotion/index.html b/categories/emotion/index.html deleted file mode 100644 index d4100eb..0000000 --- a/categories/emotion/index.html +++ /dev/null @@ -1,140 +0,0 @@ - - - - - - - -Genge 随笔 - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - -
-
- - -

2020

- - - -
- -
- 2020-02-05 -
Flag
-
-
-
- - - -
-
- -
- - -
- - - -
-
- - \ No newline at end of file diff --git "a/categories/\347\275\221\347\273\234/index.html" "b/categories/\347\275\221\347\273\234/index.html" deleted file mode 100644 index ac14d0c..0000000 --- "a/categories/\347\275\221\347\273\234/index.html" +++ /dev/null @@ -1,140 +0,0 @@ - - - - - - - -Genge 随笔 - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - - - -
- - -
- - - -
-
- - \ No newline at end of file diff --git a/friends/index.html b/friends/index.html deleted file mode 100644 index 88177b3..0000000 --- a/friends/index.html +++ /dev/null @@ -1,163 +0,0 @@ - - - - - - - -friends - Genge 随笔 - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - -
-
-
-

友情链接

-
-
- - - -
- - - -
- 777 -
-
- 字节跳动大神,算法大佬 -
-
-
- -
- - - -
- hl174 -
-
- 深圳腾讯大神,算法大佬 -
-
-
- - -
-
-
- - - - - -
- - - -
-
- - \ No newline at end of file diff --git a/index.html b/index.html deleted file mode 100644 index 004afdc..0000000 --- a/index.html +++ /dev/null @@ -1,366 +0,0 @@ - - - - - - - -Genge 随笔 - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - -
-
- - - - - - - - - - - - - - - - - - - - - -
-
- -
- - - 下一页 - -
- - - -
-
- - \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..d8b2393 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,3862 @@ +{ + "name": "hexo-site", + "version": "0.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/helper-string-parser": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", + "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==" + }, + "@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==" + }, + "@babel/parser": { + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.3.tgz", + "integrity": "sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ==" + }, + "@babel/types": { + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.3.tgz", + "integrity": "sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg==", + "requires": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + } + }, + "@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==" + }, + "@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==" + }, + "a-sync-waterfall": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/a-sync-waterfall/-/a-sync-waterfall-1.0.1.tgz", + "integrity": "sha512-RYTOHHdWipFUliRFMCS4X2Yn2X8M87V/OpSqWzKKOGhzqyUxzyVmhHDH9sAvG+ZuQf/TAOFsLCpMw09I1ufUnA==" + }, + "abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==" + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "requires": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "dependencies": { + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "requires": { + "mime-db": "1.52.0" + } + } + } + }, + "acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==" + }, + "acorn-globals": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz", + "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==", + "requires": { + "acorn": "^8.1.0", + "acorn-walk": "^8.0.2" + } + }, + "acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==" + }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "requires": { + "debug": "4" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "ajv": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.11.0.tgz", + "integrity": "sha512-nCprB/0syFYy9fVYU1ox1l2KN8S9I+tziH8D4zdZuLT3N6RMlGSGt5FSTpAiHB/Whv8Qs1cWHma1aMKZyaHRKA==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=" + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" + }, + "archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==" + }, + "are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=" + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-never": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/assert-never/-/assert-never-1.2.1.tgz", + "integrity": "sha512-TaTivMB6pYI1kXwrFlEhLeGfOqoDNdTxjCdwRfFFkEA30Eu+k48W34nlok2EYWJfFFzqaEmichdNM7th6M5HNw==" + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, + "async": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" + }, + "async-foreach": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz", + "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + }, + "aws4": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz", + "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==" + }, + "babel-walk": { + "version": "3.0.0-canary-5", + "resolved": "https://registry.npmjs.org/babel-walk/-/babel-walk-3.0.0-canary-5.tgz", + "integrity": "sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw==", + "requires": { + "@babel/types": "^7.9.6" + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "requires": { + "safe-buffer": "5.1.2" + } + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "binary-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", + "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==" + }, + "block-stream": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", + "requires": { + "inherits": "~2.0.0" + } + }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "requires": { + "fill-range": "^7.0.1" + } + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==" + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "camel-case": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.1.tgz", + "integrity": "sha512-7fa2WcG4fYFkclIvEmxBbTvmibwF2/agfEBc6q3lOpVu0A13ltLsA+Hr/8Hp6kp5f+G7hKi6t8lys6XxP+1K6Q==", + "requires": { + "pascal-case": "^3.1.1", + "tslib": "^1.10.0" + } + }, + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=" + }, + "camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "requires": { + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" + } + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "character-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz", + "integrity": "sha512-+UqJQjFEFaTAs3bNsF2j2kEN1baG/zghZbdqoYEDxGZtJo9LBzl1A+m0D4n3qKx8N2FNv8/Xp6yV9mQmBuptaw==", + "requires": { + "is-regex": "^1.0.3" + } + }, + "chokidar": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.1.tgz", + "integrity": "sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg==", + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.1.2", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.3.0" + } + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "command-exists": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz", + "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==" + }, + "commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==" + }, + "compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "requires": { + "mime-db": ">= 1.43.0 < 2" + } + }, + "compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "requires": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "connect": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", + "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", + "requires": { + "debug": "2.6.9", + "finalhandler": "1.1.2", + "parseurl": "~1.3.3", + "utils-merge": "1.0.1" + } + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" + }, + "constantinople": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-4.0.1.tgz", + "integrity": "sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==", + "requires": { + "@babel/parser": "^7.6.0", + "@babel/types": "^7.6.1" + } + }, + "copy-anything": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz", + "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==", + "requires": { + "is-what": "^3.14.1" + } + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cross-spawn": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.1.tgz", + "integrity": "sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg==", + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "css": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/css/-/css-3.0.0.tgz", + "integrity": "sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==", + "requires": { + "inherits": "^2.0.4", + "source-map": "^0.6.1", + "source-map-resolve": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "cssom": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==" + }, + "cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "requires": { + "cssom": "~0.3.6" + }, + "dependencies": { + "cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==" + } + } + }, + "cuid": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/cuid/-/cuid-2.1.8.tgz", + "integrity": "sha512-xiEMER6E7TlTPnDxrM4eRiC6TRgjNX9xzEZ5U/Se2YJKr7Mq4pJn/2XEHjl3STcSh96GmkHPcBXLES8M29wyyg==" + }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "requires": { + "array-find-index": "^1.0.1" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "data-urls": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", + "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", + "requires": { + "abab": "^2.0.6", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0" + } + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, + "decimal.js": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==" + }, + "decode-uri-component": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==" + }, + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" + }, + "deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==" + }, + "define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==" + }, + "define-properties": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", + "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", + "requires": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + }, + "destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" + }, + "doctypes": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz", + "integrity": "sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ==" + }, + "dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "dependencies": { + "entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==" + } + } + }, + "domelementtype": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz", + "integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==" + }, + "domexception": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", + "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", + "requires": { + "webidl-conversions": "^7.0.0" + } + }, + "domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "requires": { + "domelementtype": "^2.2.0" + }, + "dependencies": { + "domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==" + } + } + }, + "dompurify": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.5.tgz", + "integrity": "sha512-jggCCd+8Iqp4Tsz0nIvpcb22InKEBrGz5dw3EQJMs8HPJDsKbFIO3STYtAvCfDx26Muevn1MHVI0XxjgFfmiSA==" + }, + "domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "requires": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "dependencies": { + "domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==" + } + } + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "ejs": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz", + "integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==", + "requires": { + "jake": "^10.8.5" + } + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" + }, + "entities": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", + "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==" + }, + "errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "optional": true, + "requires": { + "prr": "~1.0.1" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "escodegen": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", + "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", + "requires": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "optional": true + } + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==" + }, + "fast-deep-equal": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", + "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==" + }, + "fast-equals": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-3.0.3.tgz", + "integrity": "sha512-NCe8qxnZFARSHGztGMZOO/PC1qa5MIFB5Hp66WdzbCRAz8U8US3bx1UTgLS49efBQPcUtO9gf5oVEY8o7y/7Kg==" + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" + }, + "filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "requires": { + "minimatch": "^5.0.1" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + } + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "fsevents": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", + "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", + "optional": true + }, + "fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "requires": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + } + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + }, + "dependencies": { + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "gaze": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", + "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", + "requires": { + "globule": "^1.0.0" + } + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" + }, + "get-intrinsic": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + } + }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=" + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "requires": { + "is-glob": "^4.0.1" + } + }, + "globule": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.0.tgz", + "integrity": "sha512-YlD4kdMqRCQHrhVdonet4TdRtv1/sZKepvoxNT4Nrhrp5HI8XFfc8kFlGlBn2myBo80aGp8Eft259mbcUJhgSg==", + "requires": { + "glob": "~7.1.1", + "lodash": "~4.17.10", + "minimatch": "~3.0.2" + }, + "dependencies": { + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, + "graceful-fs": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==" + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + }, + "har-validator": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "requires": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "requires": { + "get-intrinsic": "^1.1.1" + } + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "requires": { + "has-symbols": "^1.0.2" + } + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" + }, + "hexo": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/hexo/-/hexo-6.3.0.tgz", + "integrity": "sha512-4Jq+rWd8sYvR1YdIQyndN/9WboQ/Mqm6eax8CjrjO+ePFm2oMVafSOx9WEyJ42wcLOHjfyMfnlQhnUuNmJIpPg==", + "requires": { + "abbrev": "^1.1.1", + "archy": "^1.0.0", + "bluebird": "^3.7.2", + "hexo-cli": "^4.3.0", + "hexo-front-matter": "^3.0.0", + "hexo-fs": "^3.1.0", + "hexo-i18n": "^1.0.0", + "hexo-log": "^3.2.0", + "hexo-util": "^2.7.0", + "js-yaml": "^4.1.0", + "js-yaml-js-types": "^1.0.0", + "micromatch": "^4.0.4", + "moize": "^6.1.0", + "moment": "^2.29.1", + "moment-timezone": "^0.5.34", + "nunjucks": "^3.2.3", + "picocolors": "^1.0.0", + "pretty-hrtime": "^1.0.3", + "resolve": "^1.22.0", + "strip-ansi": "^6.0.0", + "text-table": "^0.2.0", + "tildify": "^2.0.0", + "titlecase": "^1.1.3", + "warehouse": "^4.0.2" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "dependencies": { + "entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==" + } + } + }, + "domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "requires": { + "domelementtype": "^2.2.0" + }, + "dependencies": { + "domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==" + } + } + }, + "domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "requires": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "dependencies": { + "domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==" + } + } + }, + "entities": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", + "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==" + }, + "hexo-cli": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/hexo-cli/-/hexo-cli-4.3.0.tgz", + "integrity": "sha512-lr46h1tK1RNQJAQZbzKYAWGsmqF5DLrW6xKEakqv/o9JqgdeempBjIm7HqjcZEUBpWij4EO65X6YJiDmT9LR7g==", + "requires": { + "abbrev": "^1.1.1", + "bluebird": "^3.5.5", + "chalk": "^4.0.0", + "command-exists": "^1.2.8", + "hexo-fs": "^3.0.1", + "hexo-log": "^2.0.0", + "hexo-util": "^2.0.0", + "minimist": "^1.2.5", + "resolve": "^1.11.0", + "tildify": "^2.0.0" + }, + "dependencies": { + "hexo-log": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hexo-log/-/hexo-log-2.0.0.tgz", + "integrity": "sha512-U7zdDae74pXcyhQEyNmpJdq3UI6zWKxQ7/zLoMr/d3CBRdIfB5yO8DWqKUnewfibYv0gODyTWUIhxQDWuwloow==", + "requires": { + "chalk": "^4.0.0" + } + } + } + }, + "hexo-fs": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hexo-fs/-/hexo-fs-3.1.0.tgz", + "integrity": "sha512-SfoDH7zlU9Iop+bAfEONXezbNIkpVX1QqjNCBYpapilZR+xVOCfTEdlNixanrKBbLGPb2fXqrdDBFgrKuiVGQQ==", + "requires": { + "bluebird": "^3.5.1", + "chokidar": "^3.0.0", + "graceful-fs": "^4.1.11", + "hexo-util": "^2.0.0" + } + }, + "hexo-util": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/hexo-util/-/hexo-util-2.7.0.tgz", + "integrity": "sha512-hQM3h34nhDg0bSe/Tg1lnpODvNkz7h2u0+lZGzlKL0Oufp+5KCAEUX9wal7/xC7ax3/cwEn8IuoU75kNpZLpJQ==", + "requires": { + "bluebird": "^3.5.2", + "camel-case": "^4.0.0", + "cross-spawn": "^7.0.0", + "deepmerge": "^4.2.2", + "highlight.js": "^11.0.1", + "htmlparser2": "^7.0.0", + "prismjs": "^1.17.1", + "strip-indent": "^3.0.0" + } + }, + "highlight.js": { + "version": "11.7.0", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.7.0.tgz", + "integrity": "sha512-1rRqesRFhMO/PRF+G86evnyJkCgaZFOI+Z6kdj15TA18funfoqJXvgPCLSf0SWq3SRfg1j3HlDs8o4s3EGq1oQ==" + }, + "htmlparser2": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz", + "integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==", + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.2", + "domutils": "^2.8.0", + "entities": "^3.0.1" + } + }, + "resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "requires": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + } + } + }, + "hexo-deployer-git": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/hexo-deployer-git/-/hexo-deployer-git-4.0.0.tgz", + "integrity": "sha512-28t1Q+4taB/UaBAP52W3mD/wcCwa2y2zBieUfBJFBZudbmVgiKJB5YedYILeyI5QByaUKAOwoupmdTbocdQ+CQ==", + "requires": { + "bluebird": "^3.7.2", + "hexo-fs": "^4.0.0", + "hexo-util": "^2.7.0", + "luxon": "^3.0.4", + "nunjucks": "^3.2.3", + "picocolors": "^1.0.0" + } + }, + "hexo-front-matter": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hexo-front-matter/-/hexo-front-matter-3.0.0.tgz", + "integrity": "sha512-hSQTPUmB/BCe1BFYmXRkPyLk8rqbBqHCQq+rjwwOJuEfOADrFaVK2VPZb90tJzPyXE1xSxpgCxE/AZq0CyTVwg==", + "requires": { + "js-yaml": "^4.1.0" + } + }, + "hexo-fs": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/hexo-fs/-/hexo-fs-4.1.1.tgz", + "integrity": "sha512-aDysNTyv8ElcerbFVbPLRXnYt+QDY6gAOZZ5DLbCxudY0Ywppqd+uZ03gZ2BDypIBvmNB27WYWYz76M+Yv/YXw==", + "requires": { + "bluebird": "^3.7.2", + "chokidar": "^3.5.3", + "graceful-fs": "^4.2.10", + "hexo-util": "^2.7.0" + }, + "dependencies": { + "anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "optional": true + }, + "graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "requires": { + "picomatch": "^2.2.1" + } + } + } + }, + "hexo-generator-archive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hexo-generator-archive/-/hexo-generator-archive-2.0.0.tgz", + "integrity": "sha512-KikJk7dGFbtNHOgqtLFGf5T/S8n1paGp+Gy0KfVDz+HKYhGbXOouyiZkmc3O9KrYt6ja14rmkMhq7KKGtvfehw==", + "requires": { + "hexo-pagination": "3.0.0" + } + }, + "hexo-generator-category": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hexo-generator-category/-/hexo-generator-category-2.0.0.tgz", + "integrity": "sha512-9OduRBf3WeRDa4BR0kAfRjOVHur7v3fm0NKAwbjUiqULigAdNZVZPO3cHKW2MlBbl/lI5PuWdhQ9zZ99CCCAgQ==", + "requires": { + "hexo-pagination": "3.0.0" + } + }, + "hexo-generator-feed": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hexo-generator-feed/-/hexo-generator-feed-3.0.0.tgz", + "integrity": "sha512-Jo35VSRSNeMitS2JmjCq3OHAXXYU4+JIODujHtubdG/NRj2++b3Tgyz9pwTmROx6Yxr2php/hC8og5AGZHh8UQ==", + "requires": { + "hexo-util": "^2.1.0", + "nunjucks": "^3.0.0" + } + }, + "hexo-generator-index": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hexo-generator-index/-/hexo-generator-index-3.0.0.tgz", + "integrity": "sha512-83AuNN4cWdLVi//3ugR8E3kR6rrOwhXZt+hOCm1IjtIGj353/GlrtpMHpqZHU5kqipzj4miy9dweVdukXglVWw==", + "requires": { + "hexo-pagination": "3.0.0" + } + }, + "hexo-generator-tag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hexo-generator-tag/-/hexo-generator-tag-2.0.0.tgz", + "integrity": "sha512-1px/hF3veEohWDN8jjzchQhaiz+uOStUvvMaBJC9vWOlALh30UFcapL8IrvAwwJZjFRVA+WqGgDRqoQ8+yaaFw==", + "requires": { + "hexo-pagination": "3.0.0" + } + }, + "hexo-i18n": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hexo-i18n/-/hexo-i18n-1.0.0.tgz", + "integrity": "sha512-yw90JHr7ybUHN/QOkpHmlWJj1luVk5/v8CUU5NRA0n4TFp6av8NT7ujZ10GDawgnQEdMHnN5PUfAbNIVGR6axg==", + "requires": { + "sprintf-js": "^1.0.3" + } + }, + "hexo-log": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/hexo-log/-/hexo-log-3.2.0.tgz", + "integrity": "sha512-fk7jOW3hvKiAv4Q/d8UxaQlARwcv+5KjGcnxexUrqBqyWbMCLmw7jhMHTSRLNNQpaoTlF5ff+kQkPi4yhp9iag==", + "requires": { + "picocolors": "^1.0.0" + } + }, + "hexo-pagination": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hexo-pagination/-/hexo-pagination-3.0.0.tgz", + "integrity": "sha512-8oo1iozloZo7TojPVYg4IxL3SJKCBdSJ908fTlIxIK7TWJIKdYnQlW31+12DBJ0NhVZA/lZisPObGF08wT8fKw==" + }, + "hexo-renderer-ejs": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hexo-renderer-ejs/-/hexo-renderer-ejs-2.0.0.tgz", + "integrity": "sha512-qCjE1IdwgDgv65qyb0KMVCwCdSVAkH0vwAe9XihjvaKWkmb9dtt8DgErOdqCXn0HReSyWiEVP2BrLRj3gyHwOQ==", + "requires": { + "ejs": "^3.1.6" + } + }, + "hexo-renderer-less": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/hexo-renderer-less/-/hexo-renderer-less-4.0.0.tgz", + "integrity": "sha512-wyYeDgtDUPlJzZKpc4MgJv9LvN8/MgZYXSlvn6E+/UYRm+3mU25svPWTIkImrlpr2ZimD4PkyiIPVtyEp7RK/g==", + "requires": { + "less": "^4.1.2", + "micromatch": "^4.0.4" + } + }, + "hexo-renderer-marked": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/hexo-renderer-marked/-/hexo-renderer-marked-6.0.0.tgz", + "integrity": "sha512-/B/ud8q9pNldbipuv6cPyqL+fir973+blV79n6j59M3S8LRz/4hLXwd0TA4RHxcHVrgPakeWUtiH3UWo6B6Pag==", + "requires": { + "dompurify": "^2.4.0", + "hexo-util": "^2.7.0", + "jsdom": "^20.0.1", + "marked": "^4.1.1" + }, + "dependencies": { + "dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "dependencies": { + "entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==" + } + } + }, + "domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "requires": { + "domelementtype": "^2.2.0" + }, + "dependencies": { + "domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==" + } + } + }, + "domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "requires": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "dependencies": { + "domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==" + } + } + }, + "entities": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", + "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==" + }, + "hexo-util": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/hexo-util/-/hexo-util-2.7.0.tgz", + "integrity": "sha512-hQM3h34nhDg0bSe/Tg1lnpODvNkz7h2u0+lZGzlKL0Oufp+5KCAEUX9wal7/xC7ax3/cwEn8IuoU75kNpZLpJQ==", + "requires": { + "bluebird": "^3.5.2", + "camel-case": "^4.0.0", + "cross-spawn": "^7.0.0", + "deepmerge": "^4.2.2", + "highlight.js": "^11.0.1", + "htmlparser2": "^7.0.0", + "prismjs": "^1.17.1", + "strip-indent": "^3.0.0" + } + }, + "highlight.js": { + "version": "11.7.0", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.7.0.tgz", + "integrity": "sha512-1rRqesRFhMO/PRF+G86evnyJkCgaZFOI+Z6kdj15TA18funfoqJXvgPCLSf0SWq3SRfg1j3HlDs8o4s3EGq1oQ==" + }, + "htmlparser2": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz", + "integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==", + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.2", + "domutils": "^2.8.0", + "entities": "^3.0.1" + } + } + } + }, + "hexo-renderer-pug": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hexo-renderer-pug/-/hexo-renderer-pug-3.0.0.tgz", + "integrity": "sha512-PmbLx6VkNv+mPLOe97OC4F8iTzTuj665dSYN7bZKArd4M/q7gb2tNs29VGuAOC50i9tvWY2f+tPQimf0GZ9Hyw==", + "requires": { + "pug": "^3.0.2" + } + }, + "hexo-renderer-sass": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/hexo-renderer-sass/-/hexo-renderer-sass-0.4.0.tgz", + "integrity": "sha512-goQ/r8J/2s5XnIp+dSDwWgaxHl6h/VVEsp95Jr/+sYqFfXHXDIC4evC+V1to9ezfQwOS//ZZFQptTZIKTuh8Lg==", + "requires": { + "node-sass": "^4.5.3" + } + }, + "hexo-renderer-stylus": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hexo-renderer-stylus/-/hexo-renderer-stylus-2.1.0.tgz", + "integrity": "sha512-Nef4YCr7JX8jaRaByhzXMSsWnDed+RgJj6aU/ARnYu3Bn5xz/qRz52VJG7KqD0Xuysxa9TIBdVUgNzBrSFn3DQ==", + "requires": { + "nib": "^1.2.0", + "stylus": "^0.57.0" + } + }, + "hexo-server": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hexo-server/-/hexo-server-3.0.0.tgz", + "integrity": "sha512-u4s0ty9Aew6jV+a9oMrXBwhrRpUQ0U8PWM/88a5aHgDru58VY81mVrxOFxs788NAsWQ8OvsJtF5m7mnXoRnSIA==", + "requires": { + "bluebird": "^3.5.5", + "compression": "^1.7.4", + "connect": "^3.7.0", + "mime": "^3.0.0", + "morgan": "^1.9.1", + "open": "^8.0.9", + "picocolors": "^1.0.0", + "serve-static": "^1.14.1" + } + }, + "hexo-theme-pure": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/hexo-theme-pure/-/hexo-theme-pure-1.0.2.tgz", + "integrity": "sha512-Ra2xuwWD5okpIg/Q3tg2J0M80b6EXVHDb3yU7AHxTwjDUpjQ8ZxtnUUaX1CeXN7QkeavqqhZ/ooEY+3tJUmluA==", + "requires": { + "hexo-renderer-ejs": "^1.0.0", + "hexo-renderer-less": "^2.0.2" + }, + "dependencies": { + "ejs": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.7.4.tgz", + "integrity": "sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA==" + }, + "hexo-renderer-ejs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hexo-renderer-ejs/-/hexo-renderer-ejs-1.0.0.tgz", + "integrity": "sha512-O925i69FG4NYO62oWORcPhRZZX0sPx1SXGKUS5DaR/lzajyiXH5i2sqnkj0ya0rNLXIy/D7Xmt7WbFyuQx/kKQ==", + "requires": { + "ejs": "^2.6.1" + } + }, + "hexo-renderer-less": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hexo-renderer-less/-/hexo-renderer-less-2.0.2.tgz", + "integrity": "sha512-ieBAtFm6fhWeJJzVPzCBcI94qXIrjokJ25eBg2xBsB6Tj1JGzsKqUt8dPKNIxgudYguf+HX5eViIbqGCeelS9g==", + "requires": { + "less": "^3.9.0", + "micromatch": "^4.0.2" + } + }, + "less": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/less/-/less-3.13.1.tgz", + "integrity": "sha512-SwA1aQXGUvp+P5XdZslUOhhLnClSLIjWvJhmd+Vgib5BFIr9lMNlQwmwUNOjXThF/A0x+MCYYPeWEfeWiLRnTw==", + "requires": { + "copy-anything": "^2.0.1", + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "make-dir": "^2.1.0", + "mime": "^1.4.1", + "native-request": "^1.0.5", + "source-map": "~0.6.0", + "tslib": "^1.10.0" + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "optional": true + } + } + }, + "hexo-util": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/hexo-util/-/hexo-util-2.7.0.tgz", + "integrity": "sha512-hQM3h34nhDg0bSe/Tg1lnpODvNkz7h2u0+lZGzlKL0Oufp+5KCAEUX9wal7/xC7ax3/cwEn8IuoU75kNpZLpJQ==", + "requires": { + "bluebird": "^3.5.2", + "camel-case": "^4.0.0", + "cross-spawn": "^7.0.0", + "deepmerge": "^4.2.2", + "highlight.js": "^11.0.1", + "htmlparser2": "^7.0.0", + "prismjs": "^1.17.1", + "strip-indent": "^3.0.0" + } + }, + "highlight.js": { + "version": "11.7.0", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.7.0.tgz", + "integrity": "sha512-1rRqesRFhMO/PRF+G86evnyJkCgaZFOI+Z6kdj15TA18funfoqJXvgPCLSf0SWq3SRfg1j3HlDs8o4s3EGq1oQ==" + }, + "hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" + }, + "html-encoding-sniffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", + "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", + "requires": { + "whatwg-encoding": "^2.0.0" + } + }, + "htmlparser2": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz", + "integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==", + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.2", + "domutils": "^2.8.0", + "entities": "^3.0.1" + } + }, + "http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "requires": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "dependencies": { + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" + } + } + }, + "http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "requires": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "requires": { + "agent-base": "6", + "debug": "4" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + }, + "image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==", + "optional": true + }, + "in-publish": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.0.tgz", + "integrity": "sha1-4g/146KvwmkDILbcVSaCqcf631E=" + }, + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "requires": { + "repeating": "^2.0.0" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-core-module": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "requires": { + "has": "^1.0.3" + } + }, + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==" + }, + "is-expression": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-4.0.0.tgz", + "integrity": "sha512-zMIXX63sxzG3XrkHkrAPvm/OVZVSCPNkwMHU8oTX7/U3AL78I0QXCEICXUM13BIa8TYGZ68PiTKfQz3yaTNr4A==", + "requires": { + "acorn": "^7.1.1", + "object-assign": "^4.1.1" + }, + "dependencies": { + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==" + } + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + }, + "is-finite": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", + "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + }, + "is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==" + }, + "is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==" + }, + "is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==" + }, + "is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" + }, + "is-what": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", + "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==" + }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "requires": { + "is-docker": "^2.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "jake": { + "version": "10.8.5", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz", + "integrity": "sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==", + "requires": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.1", + "minimatch": "^3.0.4" + } + }, + "js-base64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.1.tgz", + "integrity": "sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw==" + }, + "js-stringify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz", + "integrity": "sha512-rtS5ATOo2Q5k1G+DADISilDA6lv79zIiwFd6CcjuIxGKLFm5C+RLImRscVap9k55i+MOZwgliw+NejvkLuGD5g==" + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "requires": { + "argparse": "^2.0.1" + } + }, + "js-yaml-js-types": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/js-yaml-js-types/-/js-yaml-js-types-1.0.1.tgz", + "integrity": "sha512-5tpfyORs8OQ43alNERbWfYRCtWgykvzYgY46fUhrQi2+kS7N0NuuFYLZ/IrfmVm5muLTndeMublgraXiFRjEPw==", + "requires": { + "esprima": "^4.0.1" + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + }, + "jsdom": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz", + "integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==", + "requires": { + "abab": "^2.0.6", + "acorn": "^8.8.1", + "acorn-globals": "^7.0.0", + "cssom": "^0.5.0", + "cssstyle": "^2.3.0", + "data-urls": "^3.0.2", + "decimal.js": "^10.4.2", + "domexception": "^4.0.0", + "escodegen": "^2.0.0", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.1", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.2", + "parse5": "^7.1.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.1.2", + "w3c-xmlserializer": "^4.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^2.0.0", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0", + "ws": "^8.11.0", + "xml-name-validator": "^4.0.0" + }, + "dependencies": { + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "tough-cookie": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.2.tgz", + "integrity": "sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==", + "requires": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + } + } + } + }, + "json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==" + }, + "jsprim": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + } + }, + "jstransformer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", + "integrity": "sha512-C9YK3Rf8q6VAPDCCU9fnqo3mAfOH6vUGnMcP4AQAYIEpWtfGLpwOTmZ+igtdK5y+VvI2n3CyYSzy4Qh34eq24A==", + "requires": { + "is-promise": "^2.0.0", + "promise": "^7.0.1" + } + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "requires": { + "invert-kv": "^1.0.0" + } + }, + "less": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/less/-/less-4.1.3.tgz", + "integrity": "sha512-w16Xk/Ta9Hhyei0Gpz9m7VS8F28nieJaL/VyShID7cYvP6IL5oHeL6p4TXSDJqZE/lNv0oJ2pGVjJsRkfwm5FA==", + "requires": { + "copy-anything": "^2.0.1", + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "make-dir": "^2.1.0", + "mime": "^1.4.1", + "needle": "^3.1.0", + "parse-node-version": "^1.0.1", + "source-map": "~0.6.0", + "tslib": "^2.3.0" + }, + "dependencies": { + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "optional": true + }, + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + } + } + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "requires": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + } + }, + "lower-case": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.1.tgz", + "integrity": "sha512-LiWgfDLLb1dwbFQZsSglpRj+1ctGnayXz3Uv0/WO8n558JycT5fg6zkNcnW0G68Nn0aEldTFeEfmjCfmqry/rQ==", + "requires": { + "tslib": "^1.10.0" + } + }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "luxon": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.3.0.tgz", + "integrity": "sha512-An0UCfG/rSiqtAIiBPO0Y9/zAnHUZxAMiCpTd5h2smgsj7GGmcenvrvww2cqNA8/4A5ZrD1gJpHN2mIHZQF+Mg==" + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "optional": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "dependencies": { + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "optional": true + } + } + }, + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=" + }, + "marked": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==" + }, + "meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "requires": { + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" + } + }, + "micro-memoize": { + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/micro-memoize/-/micro-memoize-4.0.14.tgz", + "integrity": "sha512-2tzWP1w2Hh+r7kCYa4f//jpBEA6dAueiuLco38NxfjF9Py3KCCI7wVOTdCvOhmTC043t+ulclVBdl3v+s+UJIQ==" + }, + "micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "dependencies": { + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" + } + } + }, + "mime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==" + }, + "mime-db": { + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz", + "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==" + }, + "mime-types": { + "version": "2.1.26", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz", + "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==", + "requires": { + "mime-db": "1.43.0" + } + }, + "min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==" + }, + "minimatch": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz", + "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" + }, + "mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "requires": { + "minimist": "^1.2.6" + } + }, + "moize": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/moize/-/moize-6.1.5.tgz", + "integrity": "sha512-Fu46qKV9F8DOi2vXimR3yRw/JAJfFRQEFZeclvOFnG92AEFERqwFtu4PIxETYFtCghHGlU1itKcvvNioKgWGIw==", + "requires": { + "fast-equals": "^3.0.1", + "micro-memoize": "^4.0.11" + } + }, + "moment": { + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", + "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==" + }, + "moment-timezone": { + "version": "0.5.42", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.42.tgz", + "integrity": "sha512-tjI9goqwzkflKSTxJo+jC/W8riTFwEjjunssmFvAWlvNVApjbkJM7UHggyKO0q1Fd/kZVKY77H7C9A0XKhhAFw==", + "requires": { + "moment": "^2.29.4" + } + }, + "morgan": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", + "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", + "requires": { + "basic-auth": "~2.0.1", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-finished": "~2.3.0", + "on-headers": "~1.0.2" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "nan": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==" + }, + "native-request": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/native-request/-/native-request-1.1.0.tgz", + "integrity": "sha512-uZ5rQaeRn15XmpgE0xoPL8YWqcX90VtCFglYwAgkvKM5e8fog+vePLAhHxuuv/gRkrQxIeh5U3q9sMNUrENqWw==", + "optional": true + }, + "needle": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/needle/-/needle-3.2.0.tgz", + "integrity": "sha512-oUvzXnyLiVyVGoianLijF9O/RecZUf7TkBfimjGrLM4eQhXyeJwM6GeAWccwfQ9aa4gMCZKqhAOuLaMIcQxajQ==", + "optional": true, + "requires": { + "debug": "^3.2.6", + "iconv-lite": "^0.6.3", + "sax": "^1.2.4" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "optional": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "optional": true + } + } + }, + "negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" + }, + "nib": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/nib/-/nib-1.2.0.tgz", + "integrity": "sha512-7HgrnMl/3yOmWykueO8/D0q+0iWwe7Z+CK2Eaq/xQV8w1hK80WN1oReRQkfkrztbAAnp/nTHkUSl5EcVkor6JQ==" + }, + "no-case": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.3.tgz", + "integrity": "sha512-ehY/mVQCf9BL0gKfsJBvFJen+1V//U+0HQMPrWct40ixE4jnv0bfvxDbWtAHL9EcaPEOJHVVYKoQn1TlZUB8Tw==", + "requires": { + "lower-case": "^2.0.1", + "tslib": "^1.10.0" + } + }, + "node-gyp": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz", + "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==", + "requires": { + "fstream": "^1.0.0", + "glob": "^7.0.3", + "graceful-fs": "^4.1.2", + "mkdirp": "^0.5.0", + "nopt": "2 || 3", + "npmlog": "0 || 1 || 2 || 3 || 4", + "osenv": "0", + "request": "^2.87.0", + "rimraf": "2", + "semver": "~5.3.0", + "tar": "^2.0.0", + "which": "1" + }, + "dependencies": { + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "semver": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=" + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "node-sass": { + "version": "4.13.1", + "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.13.1.tgz", + "integrity": "sha512-TTWFx+ZhyDx1Biiez2nB0L3YrCZ/8oHagaDalbuBSlqXgUPsdkUSzJsVxeDO9LtPB49+Fh3WQl3slABo6AotNw==", + "requires": { + "async-foreach": "^0.1.3", + "chalk": "^1.1.1", + "cross-spawn": "^3.0.0", + "gaze": "^1.0.0", + "get-stdin": "^4.0.1", + "glob": "^7.0.3", + "in-publish": "^2.0.0", + "lodash": "^4.17.15", + "meow": "^3.7.0", + "mkdirp": "^0.5.1", + "nan": "^2.13.2", + "node-gyp": "^3.8.0", + "npmlog": "^4.0.0", + "request": "^2.88.0", + "sass-graph": "^2.2.4", + "stdout-stream": "^1.4.0", + "true-case-path": "^1.0.2" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "cross-spawn": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz", + "integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=", + "requires": { + "lru-cache": "^4.0.1", + "which": "^1.2.9" + } + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "nopt": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-2.1.2.tgz", + "integrity": "sha1-bMzZd7gBMqB3MdbozljCyDA8+a8=", + "requires": { + "abbrev": "1" + } + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "nunjucks": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.2.3.tgz", + "integrity": "sha512-psb6xjLj47+fE76JdZwskvwG4MYsQKXUtMsPh6U0YMvmyjRtKRFcxnlXGWglNybtNTNVmGdp94K62/+NjF5FDQ==", + "requires": { + "a-sync-waterfall": "^1.0.0", + "asap": "^2.0.3", + "commander": "^5.1.0" + } + }, + "nwsapi": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.2.tgz", + "integrity": "sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw==" + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + }, + "object.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + } + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "requires": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + } + }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" + }, + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "requires": { + "lcid": "^1.0.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + }, + "osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "requires": { + "error-ex": "^1.2.0" + } + }, + "parse-node-version": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==" + }, + "parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "requires": { + "entities": "^4.4.0" + }, + "dependencies": { + "entities": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", + "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==" + } + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "pascal-case": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.1.tgz", + "integrity": "sha512-XIeHKqIrsquVTQL2crjq3NfJUxmdLasn3TYOU0VBM+UX2a6ztAWBlJQBePLGY7VHW8+2dRadeIPK5+KImwTxQA==", + "requires": { + "no-case": "^3.0.3", + "tslib": "^1.10.0" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "requires": { + "pinkie-promise": "^2.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "picomatch": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.1.tgz", + "integrity": "sha512-ISBaA8xQNmwELC7eOjqFKMESB2VIqt4PPDD0nsS95b/9dZXvVKOlz9keMSnoGGKcOHXfTvDD6WMaRoSc9UuhRA==" + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "requires": { + "pinkie": "^2.0.0" + } + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==" + }, + "pretty-hrtime": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "integrity": "sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==" + }, + "prismjs": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", + "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==" + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "requires": { + "asap": "~2.0.3" + } + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", + "optional": true + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" + }, + "psl": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.7.0.tgz", + "integrity": "sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ==" + }, + "pug": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pug/-/pug-3.0.2.tgz", + "integrity": "sha512-bp0I/hiK1D1vChHh6EfDxtndHji55XP/ZJKwsRqrz6lRia6ZC2OZbdAymlxdVFwd1L70ebrVJw4/eZ79skrIaw==", + "requires": { + "pug-code-gen": "^3.0.2", + "pug-filters": "^4.0.0", + "pug-lexer": "^5.0.1", + "pug-linker": "^4.0.0", + "pug-load": "^3.0.0", + "pug-parser": "^6.0.0", + "pug-runtime": "^3.0.1", + "pug-strip-comments": "^2.0.0" + } + }, + "pug-attrs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pug-attrs/-/pug-attrs-3.0.0.tgz", + "integrity": "sha512-azINV9dUtzPMFQktvTXciNAfAuVh/L/JCl0vtPCwvOA21uZrC08K/UnmrL+SXGEVc1FwzjW62+xw5S/uaLj6cA==", + "requires": { + "constantinople": "^4.0.1", + "js-stringify": "^1.0.2", + "pug-runtime": "^3.0.0" + } + }, + "pug-code-gen": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-3.0.2.tgz", + "integrity": "sha512-nJMhW16MbiGRiyR4miDTQMRWDgKplnHyeLvioEJYbk1RsPI3FuA3saEP8uwnTb2nTJEKBU90NFVWJBk4OU5qyg==", + "requires": { + "constantinople": "^4.0.1", + "doctypes": "^1.1.0", + "js-stringify": "^1.0.2", + "pug-attrs": "^3.0.0", + "pug-error": "^2.0.0", + "pug-runtime": "^3.0.0", + "void-elements": "^3.1.0", + "with": "^7.0.0" + } + }, + "pug-error": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-2.0.0.tgz", + "integrity": "sha512-sjiUsi9M4RAGHktC1drQfCr5C5eriu24Lfbt4s+7SykztEOwVZtbFk1RRq0tzLxcMxMYTBR+zMQaG07J/btayQ==" + }, + "pug-filters": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pug-filters/-/pug-filters-4.0.0.tgz", + "integrity": "sha512-yeNFtq5Yxmfz0f9z2rMXGw/8/4i1cCFecw/Q7+D0V2DdtII5UvqE12VaZ2AY7ri6o5RNXiweGH79OCq+2RQU4A==", + "requires": { + "constantinople": "^4.0.1", + "jstransformer": "1.0.0", + "pug-error": "^2.0.0", + "pug-walk": "^2.0.0", + "resolve": "^1.15.1" + }, + "dependencies": { + "resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "requires": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + } + } + }, + "pug-lexer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pug-lexer/-/pug-lexer-5.0.1.tgz", + "integrity": "sha512-0I6C62+keXlZPZkOJeVam9aBLVP2EnbeDw3An+k0/QlqdwH6rv8284nko14Na7c0TtqtogfWXcRoFE4O4Ff20w==", + "requires": { + "character-parser": "^2.2.0", + "is-expression": "^4.0.0", + "pug-error": "^2.0.0" + } + }, + "pug-linker": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pug-linker/-/pug-linker-4.0.0.tgz", + "integrity": "sha512-gjD1yzp0yxbQqnzBAdlhbgoJL5qIFJw78juN1NpTLt/mfPJ5VgC4BvkoD3G23qKzJtIIXBbcCt6FioLSFLOHdw==", + "requires": { + "pug-error": "^2.0.0", + "pug-walk": "^2.0.0" + } + }, + "pug-load": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pug-load/-/pug-load-3.0.0.tgz", + "integrity": "sha512-OCjTEnhLWZBvS4zni/WUMjH2YSUosnsmjGBB1An7CsKQarYSWQ0GCVyd4eQPMFJqZ8w9xgs01QdiZXKVjk92EQ==", + "requires": { + "object-assign": "^4.1.1", + "pug-walk": "^2.0.0" + } + }, + "pug-parser": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-6.0.0.tgz", + "integrity": "sha512-ukiYM/9cH6Cml+AOl5kETtM9NR3WulyVP2y4HOU45DyMim1IeP/OOiyEWRr6qk5I5klpsBnbuHpwKmTx6WURnw==", + "requires": { + "pug-error": "^2.0.0", + "token-stream": "1.0.0" + } + }, + "pug-runtime": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/pug-runtime/-/pug-runtime-3.0.1.tgz", + "integrity": "sha512-L50zbvrQ35TkpHwv0G6aLSuueDRwc/97XdY8kL3tOT0FmhgG7UypU3VztfV/LATAvmUfYi4wNxSajhSAeNN+Kg==" + }, + "pug-strip-comments": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-2.0.0.tgz", + "integrity": "sha512-zo8DsDpH7eTkPHCXFeAk1xZXJbyoTfdPlNR0bK7rpOMuhBYb0f5qUVCO1xlsitYd3w5FQTK7zpNVKb3rZoUrrQ==", + "requires": { + "pug-error": "^2.0.0" + } + }, + "pug-walk": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-2.0.0.tgz", + "integrity": "sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ==" + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readdirp": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.3.0.tgz", + "integrity": "sha512-zz0pAkSPOXXm1viEwygWIPSPkcBYjW1xU5j/JBh5t9bGCJwa6f9+BJa6VaB2g+b55yVrmXzqkyLf4xaWYM0IkQ==", + "requires": { + "picomatch": "^2.0.7" + } + }, + "redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "requires": { + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" + }, + "dependencies": { + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "requires": { + "get-stdin": "^4.0.1" + } + } + } + }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "requires": { + "is-finite": "^1.0.0" + } + }, + "request": { + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" + }, + "resolve": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.0.tgz", + "integrity": "sha512-+hTmAldEGE80U2wJJDC1lebb5jWqvTYAfm3YZ1ckk1gBr0MnCqUKlwK1e+anaFljIl+F5tR5IoZcm4ZDA1zMQw==", + "requires": { + "path-parse": "^1.0.6" + } + }, + "rfdc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==" + }, + "rimraf": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", + "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=", + "requires": { + "glob": "^6.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sass-graph": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.4.tgz", + "integrity": "sha1-E/vWPNHK8JCLn9k0dq1DpR0eC0k=", + "requires": { + "glob": "^7.0.0", + "lodash": "^4.0.0", + "scss-tokenizer": "^0.2.3", + "yargs": "^7.0.0" + }, + "dependencies": { + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=" + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "yargs": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz", + "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=", + "requires": { + "camelcase": "^3.0.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.2", + "which-module": "^1.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^5.0.0" + }, + "dependencies": { + "y18n": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", + "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==" + }, + "yargs-parser": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.1.tgz", + "integrity": "sha512-wpav5XYiddjXxirPoCTUPbqM0PXvJ9hiBMvuJgInvo4/lAOTZzUprArw17q2O1P2+GHhbBr18/iQwjL5Z9BqfA==", + "requires": { + "camelcase": "^3.0.0", + "object.assign": "^4.1.0" + } + } + } + } + } + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "requires": { + "xmlchars": "^2.2.0" + } + }, + "scss-tokenizer": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz", + "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=", + "requires": { + "js-base64": "^2.1.8", + "source-map": "^0.4.2" + }, + "dependencies": { + "source-map": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "requires": { + "amdefine": ">=0.0.4" + } + } + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + }, + "send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "requires": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "dependencies": { + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "requires": { + "ee-first": "1.1.1" + } + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" + } + } + }, + "serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "optional": true + }, + "source-map-resolve": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz", + "integrity": "sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==", + "requires": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0" + } + }, + "spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==" + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==" + }, + "sprintf-js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==" + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==" + }, + "stdout-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz", + "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==", + "requires": { + "readable-stream": "^2.0.1" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + } + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "requires": { + "is-utf8": "^0.2.0" + } + }, + "strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "requires": { + "min-indent": "^1.0.0" + } + }, + "stylus": { + "version": "0.57.0", + "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.57.0.tgz", + "integrity": "sha512-yOI6G8WYfr0q8v8rRvE91wbxFU+rJPo760Va4MF6K0I6BZjO4r+xSynkvyPBP9tV1CIEUeRsiidjIs2rzb1CnQ==", + "requires": { + "css": "^3.0.0", + "debug": "^4.3.2", + "glob": "^7.1.6", + "safer-buffer": "^2.1.2", + "sax": "~1.2.4", + "source-map": "^0.7.3" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==" + } + } + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "requires": { + "has-flag": "^4.0.0" + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" + }, + "symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" + }, + "tar": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz", + "integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==", + "requires": { + "block-stream": "*", + "fstream": "^1.0.12", + "inherits": "2" + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" + }, + "through2": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "requires": { + "readable-stream": "3" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "tildify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tildify/-/tildify-2.0.0.tgz", + "integrity": "sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw==" + }, + "titlecase": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/titlecase/-/titlecase-1.1.3.tgz", + "integrity": "sha512-pQX4oiemzjBEELPqgK4WE+q0yhAqjp/yzusGtlSJsOuiDys0RQxggepYmo0BuegIDppYS3b3cpdegRwkpyN3hw==" + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==" + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "requires": { + "is-number": "^7.0.0" + } + }, + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" + }, + "token-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-1.0.0.tgz", + "integrity": "sha512-VSsyNPPW74RpHwR8Fc21uubwHY7wMDeJLys2IX5zJNih+OnAnaifKHo+1LHT7DAdloQ7apeaaWg8l7qnf/TnEg==" + }, + "tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + } + } + }, + "tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "requires": { + "punycode": "^2.1.1" + } + }, + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=" + }, + "true-case-path": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz", + "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==", + "requires": { + "glob": "^7.1.2" + }, + "dependencies": { + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, + "tslib": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", + "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==" + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==" + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "requires": { + "punycode": "^2.1.0" + } + }, + "url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "void-elements": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", + "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==" + }, + "w3c-xmlserializer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", + "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==", + "requires": { + "xml-name-validator": "^4.0.0" + } + }, + "warehouse": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/warehouse/-/warehouse-4.0.2.tgz", + "integrity": "sha512-GixS7SolBGu81rnxYM6bScxdElLM97Jx/kr0a6B6PGBWFqvHeuWFj7QbgEX1YWZSxiJt/aR6dBVQKC/PvvihdQ==", + "requires": { + "bluebird": "^3.2.2", + "cuid": "^2.1.4", + "graceful-fs": "^4.1.3", + "hexo-log": "^3.0.0", + "is-plain-object": "^5.0.0", + "jsonparse": "^1.3.1", + "rfdc": "^1.1.4", + "through2": "^4.0.2" + } + }, + "webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==" + }, + "whatwg-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", + "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "requires": { + "iconv-lite": "0.6.3" + } + }, + "whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==" + }, + "whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "requires": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=" + }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "with": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/with/-/with-7.0.2.tgz", + "integrity": "sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==", + "requires": { + "@babel/parser": "^7.9.6", + "@babel/types": "^7.9.6", + "assert-never": "^1.2.1", + "babel-walk": "3.0.0-canary-5" + } + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==" + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "ws": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", + "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==" + }, + "xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==" + }, + "xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..382e05e --- /dev/null +++ b/package.json @@ -0,0 +1,31 @@ +{ + "name": "hexo-site", + "version": "0.0.0", + "private": true, + "scripts": { + "build": "hexo generate", + "clean": "hexo clean", + "deploy": "hexo deploy", + "server": "hexo server" + }, + "hexo": { + "version": "6.3.0" + }, + "dependencies": { + "hexo": "^6.3.0", + "hexo-deployer-git": "^4.0.0", + "hexo-generator-archive": "^2.0.0", + "hexo-generator-category": "^2.0.0", + "hexo-generator-feed": "^3.0.0", + "hexo-generator-index": "^3.0.0", + "hexo-generator-tag": "^2.0.0", + "hexo-renderer-ejs": "^2.0.0", + "hexo-renderer-less": "^4.0.0", + "hexo-renderer-marked": "^6.0.0", + "hexo-renderer-pug": "^3.0.0", + "hexo-renderer-sass": "^0.4.0", + "hexo-renderer-stylus": "^2.1.0", + "hexo-server": "^3.0.0", + "hexo-theme-pure": "^1.0.2" + } +} diff --git a/page/2/index.html b/page/2/index.html deleted file mode 100644 index d84a261..0000000 --- a/page/2/index.html +++ /dev/null @@ -1,222 +0,0 @@ - - - - - - - -Genge 随笔 - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - -
-
- - - - - - - - - -
-
- -
- - 上一页 - - -
- - - -
-
- - \ No newline at end of file diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..5d2f001 --- /dev/null +++ b/readme.md @@ -0,0 +1,54 @@ +## 项目目录 +- scaffolds +脚手架, 相当于模板 + +- source +文章源码,这里主要是markdown文件 + +- themes +主题文件, 通过_config.yml 文件指定具体使用哪个主题 + +- _config.yml +配置文件 + +## Theme主题 + +``` +git clone https://github.com/renbaoshuo/hexo-theme-pure.git themes/pure + +``` + +## 安装 + +``` +npm install -g hexo-cli +npm install hexo-deployer-git +npm install +``` + +## Hexo +Usage: hexo +``` +Commands: + clean Remove generated files and cache. + config Get or set configurations. + deploy Deploy your website. + generate Generate static files. + help Get help on a command. + init Create a new Hexo folder. + list List the information of the site + migrate Migrate your site from other system to Hexo. + new Create a new post. + publish Moves a draft post from _drafts to _posts folder. + render Render files with renderer plugins. + server Start the server. + version Display version information. + +Global Options: + --config Specify config file instead of using _config.yml + --cwd Specify the CWD + --debug Display all verbose messages in the terminal + --draft Display draft posts + --safe Disable all plugins and scripts + --silent Hide output on console +``` \ No newline at end of file diff --git a/scaffolds/draft.md b/scaffolds/draft.md new file mode 100644 index 0000000..498e95b --- /dev/null +++ b/scaffolds/draft.md @@ -0,0 +1,4 @@ +--- +title: {{ title }} +tags: +--- diff --git a/scaffolds/page.md b/scaffolds/page.md new file mode 100644 index 0000000..f01ba3c --- /dev/null +++ b/scaffolds/page.md @@ -0,0 +1,4 @@ +--- +title: {{ title }} +date: {{ date }} +--- diff --git a/scaffolds/post.md b/scaffolds/post.md new file mode 100644 index 0000000..1f9b9a4 --- /dev/null +++ b/scaffolds/post.md @@ -0,0 +1,5 @@ +--- +title: {{ title }} +date: {{ date }} +tags: +--- diff --git a/source/_posts/Flag.md b/source/_posts/Flag.md new file mode 100644 index 0000000..1cdb882 --- /dev/null +++ b/source/_posts/Flag.md @@ -0,0 +1,12 @@ +--- +title: Flag +date: 2020-02-05 14:54:05 +tags: flag +categories: emotion +--- + +## **重要的事情说三遍** + +- 做完功能要自测!!! +- 做完功能要自测!!! +- 做完功能要自测!!! \ No newline at end of file diff --git "a/source/_posts/Linux shell\350\257\255\350\250\200\342\200\224\342\200\224dash\345\222\214bash.md" "b/source/_posts/Linux shell\350\257\255\350\250\200\342\200\224\342\200\224dash\345\222\214bash.md" new file mode 100644 index 0000000..e18f600 --- /dev/null +++ "b/source/_posts/Linux shell\350\257\255\350\250\200\342\200\224\342\200\224dash\345\222\214bash.md" @@ -0,0 +1,122 @@ +--- +title: Linux shell语言——dash和bash +date: 2020-01-23 15:13:45 +tags: linux +categories: Shell +--- + +什么是bash ? + +Bash(GNU Bourne-Again Shell)是许多Linux平台的内定Shell,事实上,还有许多传统UNIX上用的Shell,像tcsh、csh、ash、bsh、ksh等等。 + +GNU/Linux 操作系统中的 /bin/sh 本是 bash (Bourne-Again Shell) 的符号链接,但鉴于 bash 过于复杂,有人把 bash 从 NetBSD 移植到 Linux 并更名为 dash (Debian Almquist Shell),并建议将 /bin/sh 指向它,以获得更快的脚本执行速度。Dash Shell 比 Bash Shell 小的多,符合POSIX标准。 + +Debian和Ubuntu中,/bin/sh默认已经指向dash,这是一个不同于bash的shell,它主要是为了执行脚本而出现,而不是交互,它速度更快,但功能相比bash要少很多,语法严格遵守POSIX标准。 + +就是这个倒霉的dash解释器使得我按照bash语法写的shell 脚本不能运行。 + +要知道自己的/bin/sh指向何种解释器,可以用 ls /bin/sh -al 命令查看: +``` +$ ls /bin/sh -al +lrwxrwxrwx 1 root root 4 11月 16 15:33 /bin/sh -> bash +``` + +以上结果就表示当前系统用的是dash解释器。 + +切换到bash的方式其实挺简单的,关键是一直没找出这个原因…… + +修改默认的sh,可以采用命令`sudo dpkg-reconfigure dash` + +会出现一个图片状的配置菜单,选no就可以了 + +再次检查一下, ls /bin/sh -al 发现软链接指向/bin/bash + +         lrwxrwxrwx 1 root root 4 11月 16 15:33 /bin/sh -> bash + + + +注:dash 和 bash 语法上的主要的区别有: + +1. 定义函数 + +bash: function在bash中为关键字 + +dash: dash中没有function这个关键字 + +2. select var in list; do command; done + +bash:支持 + +dash:不支持, 替代方法:采用while+read+case来实现 + +3. echo {0..10} + +bash:支持{n..m}展开 + +dash:不支持,替代方法, 采用seq外部命令 + +4. here string + +bash:支持here string + +dash:不支持, 替代方法:可采用here documents + +5. >&word重定向标准输出和标准错误 + +bash: 当word为非数字时,>&word变成重定向标准错误和标准输出到文件word + +dash: >&word, word不支持非数字, 替代方法: >word 2>&1; 常见用法 >/dev/null 2>&1 + +6. 数组 + +bash: 支持数组, bash4支持关联数组 + +dash: 不支持数组,替代方法, 采用变量名+序号来实现类似的效果 + +7. 子字符串扩展 + +bash: 支持${parameter:offset:length},${parameter:offset} + +dash: 不支持, 替代方法:采用expr或cut外部命令代替 + +8. 大小写转换 + +bash: 支持${parameter^pattern},${parameter^^pattern},${parameter,pattern},${parameter,,pattern} + +dash: 不支持,替代方法:采用tr/sed/awk等外部命令转换 + +9. 进程替换<(command), >(command) + +bash: 支持进程替换 + +dash: 不支持, 替代方法, 通过临时文件中转 + +10. [ string1 = string2 ] 和 [ string1 == string2 ] + +bash: 支持两者 + +dash: 只支持= + +11. [[ 加强版test + +bash: 支持[[ ]], 可实现正则匹配等强大功能 + +dash: 不支持[[ ]], 替代方法,采用外部命令 + +12. for (( expr1 ; expr2 ; expr3 )) ; do list ; done + +bash: 支持C语言格式的for循环 + +dash: 不支持该格式的for, 替代方法,用while+$((expression))实现 + +13. let命令和((expression)) + +bash: 有内置命令let, 也支持((expression))方式 + +dash: 不支持,替代方法,采用$((expression))或者外部命令做计算 + +14. $((expression)) + +bash: 支持id++,id--,++id,--id这样到表达式 + +dash: 不支持++,--, 替代方法:id+=1,id-=1, id=id+1,id=id-1 diff --git a/source/_posts/Redis-Guide.md b/source/_posts/Redis-Guide.md new file mode 100644 index 0000000..760881f --- /dev/null +++ b/source/_posts/Redis-Guide.md @@ -0,0 +1,495 @@ +--- +title: 'Redis Guide 使用手册' +date: 2020-07-10 18:28:44 +tags: Redis +categories: Redis +--- + +# Redis 数据结构与应用 + +## 普通字符串 + +### SET 命令 + +1. SET KEY VALUE 设置 +2. NX 不存在才设置 可用于实现分布式锁 +3. XX 存在才设置 + +### GET 命令 + +1. GET KEY 返回 VALUE 值 +2. GETSET 返回旧 OLD_VALUE 值 , 并且设置新 NEW_VALUE + +### MSET 命令 + +1. MSET KEY1 VALUE1 [ KEY2 VALUE2 ...] 一次设置多个键值对 + +### MGET 命令 + +1. MGET KEY1 [ KEY2 ...] 一次设置多个键值对 + 返回VALUE列表, 这在批量获取多个键值对的时候非常实用, 业务层不用做封装 + +### MSETNX 命令 + +1. MSETNX KEY VALUE [ KEY2 VALUE2 ...] + 设置多个键值对, 这是一个原子操作, 当且仅当所有KEY都不存在的时候才会成功 + +### STRLEN 命令 + +1. STRLEN KEY 获取值的字节长度 + +### GETRANGE 命令 + +1. GETRANGE KEY start end + 根据指定索引范围设置值,相当于取值的子串 + +### SETRANGE 命令 + +1. SETRANGE KEY startIndex substitute + 根据指定开始索引,开始替换新串substitute; 当startIndex大于值长度(len - 1), redis会自动扩展值长度,并且填充空字节(\x00) + +### APPEND 命令 + +1. APPEND KEY suffix 对值进行追加操作, 并返回新串长度 + 当KEY不存在, 那么APPEND命令等价于SET操作 + +### INCRBY DECRBY 命令 + +1. INCRBY/DECRBY KEY incrment 当存储的值能够被解释为整数时, 可以使用这两个命令来对值进行加或减 + 当KEY不存在,那么会被初始化为0,然后再进行操作 + +### INCR DECR 命令 + +1. INCR/DECR KEY incrment 当存储的值能够被解释为整数时, 可以使用这两个命令来对值进行加或减1 + 当KEY不存在,那么会被初始化为0,然后再进行操作 + +### INCRBYFLOAT 命令 + +1. INCRBYFLOAT KEY incrment, 命令与INCRBY无差别,主要是针对的是浮点数 + 但是没有DECRBYFLOAT命令,可以通过设置incrment为负数来实现减法 + INCRBYFLOAT 命令对值的格式更加宽松,那么值是整数的时候,命令与INCRBY等同,存储的值也会是整数 + 注意Redis在处理浮点数的时候,小数位长度是有限制,最长为17位,对于大部分应用是足够的 + + +## 散列 + +### 结构 + +| KEY | FIELD | VALUE | +| :----: | :----: | :----: | +|article | title | "Hello World" | +| | content | "son of bitch" | +| | author | genge | +| | created_at | 2010-10-10 | + +### HSET 命令 + +1. HSET HASHKEY FIELD VALUE + 如果key或者field不存在, 那么会创建,返回值为1 + 如果field存在, 那么会更新,返回值为0 + +### HSETNX 命令 + +1. HSETNX HASH FIELD VALUE + 只在字段不存在的时候设置值,返回值为1,设置失败返回0 + +### HGET 命令 + +1. HGET HASH FIELD VALUE + 获取字段值 + +### HINCRBY or HINCRFLOATBY + +1. HINCRBY HASH FIELD INCRMENT 对数值进行加减 + + +### HSTRLEN 命令 + +1. HSTRLEN HASH FIELD 获取字段值长度 + +### HEXISTS 命令 + +1. 检查字段值是否存在 + +### HDEL 命令 + +1. HDEL HASH FILED 删除字段 + +### HLEN 命令 + +1. HLEN HASH 获取散列表字段个数 + +### HMSET/HMGET 系列命令 + +1. 效果同MSET/MGET + +### HKEYS/HVALS/HGETALL 命令 + +1. 获取散列表中所有字段FIELD +2. 获取散列表中所有VALUE +3. 获取散列表中所有FIELD和VALUE + 格式为数组: FIELD1:VALUE1:FILED2:VALUE2...... + +## List列表结构 + +### Redis List列表是一种线性的有序结构 + +### LPUSH 命令 + +1. LPUSH KEY VALUE0 [ VALUE1 VALUE2 ...] 返回推入会列表元素个数 + 向列表左端新增n个VALUE, 'L' 表示是left 而不是 list + +### RPUSH 命令 + +1. RPUSH KEY VALUE0 [ VALUE1 VALUE2 ...] 返回推入会列表元素个数 + 向列表右端新增n个VALUE, 'R' 表示是right + +### RPUSHX/LPUSHX 命令 + +1. 与上两个命令同,但是在列表KEY不存在的时候的结果不一样,这两个命令在列表KEY不存在的时候会推入失败, 返回0 + +### LPOP/RPOP + +1. LPOP/RPOP LISTKEY 弹出最左边/右边的元素, 返回POP后的元素 + +### RPOPLPUSH 命令 + +1. RPOPLPUSH source target 返回被弹出的元素值 + 将源source列表最右端元素弹出,插入到目标target列表最左端 + 其中source和target可以相同,即将列表首尾对调 + 当source 为空时,执行会失败,返回空 + +### LLEN 命令 + +1. 获取列表长度 + +### LINDEX 命令 + +1. LINDEX list index 获取列表指定索引元素 + index为正数: 左端为0, 范围为 0 -- N-1 + index为负数: 右端为-1, 范围为 -N -- -1 + +### LRANGE 命令 + +1. 获取指定索引范围内的所有元素 + LRANGE list start end + LRANGE list 0 -1 获取全部元素 + 当start和end都超出范围时,将会返回空列表;当只有一个超出时,Redis会对超出的索引进行修正,开始索引超出会被修正为0,结束索引会被修正为-1 + +### LSET 命令 + +1. LSET list index new_value + 对列表指定索引元素值更新 + +### LINSERT 命令 + +1. LINSERT list BEFORE/AFTER target_element new_element + +### LTRIM 裁剪 + +1. LTRIM list start index 删除索引范围外所有元素 + +### LREM 移除 + +1. LREM list count element 返回被移除的元素数量 + - 如果count等于0,那么命令将会移除list中所有值等于element的元素 + - 如果count等于正数,那么命令会从左端开始扫描,移除列表中值为element的count个元素 + - 如果count等于负数,那么命令会从右端开始扫描,移除列表中值为element的abs(count)个元素 + +### BLPOP 阻塞式左端弹出 + +1. BLPOP list1 [ list2 list3 ...] timeout + - 命令会按照传入的列表从左至右挨个检查是否为空,如果发现某个列表不为空,那么执行LPOP操作,返回值为两个元素的数组,第一个元素是被弹出的列表list名,第二个元素是被弹出的元素值; + - 如果当前传入的所有list为空,那么Redis将会阻塞等待直至timeout超时,返回空值,超时时间单位为秒,设置为0时表示会一直等待 + - 如果当前有多个客户端因为某个列表空而阻塞,那么按照先阻塞先服务原则进行唤醒 + - 这个命令只会当前Redis客户端 + +### BRPOP 命令 与上同 + +### BRPOPLPUSH 阻塞式弹出和推入操作 与上同 + +1. 可用于实现带有阻塞式的消息队列 + +## 无序集合Set + +### 数据结构 + +说明无序集合,集合中元素不重复 + +### SADD 命令 + +SADD set element [ element ...] + +返回值为新增元素个数,会去重 + +### SREM 命令 + +SREM set element [element ...] +移除一个或多个元素,返回真实移除元素的个数 + +### SMOVE 命令 + +`SMOVE source target element` 将指定元素从source移除,并且加入到目标集合,当source中不存在element的时候会返回失败 + +### SMEMBERS 命令 + +`SMEMBERS set` 获取集合所有元素 + +### SCARD 命令 + +1. `SCARD set` 获取集合元素个数 + +### SISMEMBER 命令 + +1. `SISMEMBER set element` 判断指定元素是否存在集合中 + +### SRANDMEMBER 命令 + +1. `SRANDMEMBER SET [count]` 随机获取集合汇总count个元素,count默认值为1 + +2. count为正数时候,返回随机不重复的min(count, SCARD) 个元素,属于不放回随机抽取 +3. count为负数的时候,随机的机制发生变化,属于放回随机抽取,也就是说返回集合有可能出现重复的元素 + +### SPOP 命令 + +1. `SPOP set [count]` 随机的重集中移除count个元素,返回被移除的元素集合 + +### SINTER/SINTERSTORE 命令 + +1. `SINTER set [set]` 求多个集合的交集 + +2. `SINTER dest_set set [set ]` 求多个集合的交集,并将结果存储到新的集合中,返回新集合的元素个数 + +### SUNION/SUNIONSTORE 命令 + +1. 并集, 含义同上 + +### SDIFF/SDIFFSTORE 命令 + +1. 差集,含义同上 +2. 集合操作都非常消耗性能,可能导致Redis主线程阻塞 + +## 有序集合Sorted SET + +### 对应数据结构 + +1. 同时具有有序和集合的性质 +2. 每个元素都由一个成员和一个与成员相关联的分值score组成 +3. 排行榜最佳实现 + +`sorted set` +|score | member | +|:----: | :----: | +| 1 | genge | +| 3 | apple | +| 7 | inuby | +| 7 | oiuby | +| 19| qwmok | + +### ZADD 命令 + +1. `ZADD sorted_set [CH] score element [score element]` 向有序集合中添加元素 +2. 默认返回新添加元素的个数,当命令带 CH 的时候返回修改元素的个数 + +### ZREM 命令 + +1. `ZREM sorted_set member [member]` 移除指定元素 返回真实被移除的元素个数 + +### ZSCORE 命令 + +1. `ZSCORE sorted_set member` 获取指定元素的分值 + +### ZINCRBY 命令 + +1. `ZINCRBY sorted_set increment_score memeber` 对指定元素的分值加减 +2. 当member不存在的时候,命令等同于ZADD + +### ZCARD 命令 + +1. 获取有序集合元素个数 + +### ZRANK/ZREVRANK 命令 + +1. `ZRANK sorted_set member` 指定元素的的正排名 (从小到大) +2. `ZREVRANK sorted_set member` 指定元素的的排名 (从大到小) + +### ZRANGE / ZREVRANGE 命令 + +1. 获取指定范围内成员 `ZRANGE/ZREVRANGE sorted_set start end` +2. start 和 end均可接受负值, 含义是排名 +3. `ZRANGE/ZREVRANGE sorted_set start end WITHSCORES` 会返回分值和元素值 + +### ZRANGEBYSCORE/ZREVRANGEBYSCORE 命令 + +1. `ZRANGEBYSCORE/ZREVRANGEBYSCORE sorted_set min max/max min` 获取指定范围分数内的成员 +2. `WITHSCORES` 可以附带返回分数值 +3. `LIMIT offset count` 可以限制返回元素的数量, offset为起止偏移量,count为最大返回数量 +4. `(min (max` 使用左括号的表示开区间,默认是闭区间 +5. `-inf +inf`来表示负无穷和正无穷 + +### ZCOUNT 命令 + +1. `ZCOUNT sorted_set min max` 获取指定分值范围内的成员数量 +2. 和上面的RAGNE命令相同,都支持开闭区间无穷等设置 + +### ZREMRANGEBYRANK 命令 + +1. `ZREMRANGEBYRANK sorted_set start end` 根据给定的排名区间来移除成员 +2. 参数支持负值,表示倒数排名 + +### ZREMRANGEBYSCORE 命令 + +1. `ZREMRANGEBYSCORE sorted_set start end` 根据给定的分数区间来移除成员 +2. 和上面的RAGNE命令相同,都支持开闭区间无穷等设置 + +### ZINTERSTORE/ZUNIONSTORE 命令 + +1. `ZINTERSTORE/ZUNIONSTORE destination numbers sorted_set [sorted_set ]` 求多个集合的交集和并集 +2. numbers为sorted_set 参数的个数 +3. 返回交集元素或者并集元素个数 +4. 集合元素的分值是由两个集合分值的和 +5. `[AGGREGATE SUM/MIN/MAX]` 可以通过设置聚合函数来控制分值(求和/最小值/最大值) +6. `[WEIGHTS w1 w2 w3]` 可以为每个集合设置权重,这样计算方式将会是分值乘以权重再相加 +7. 除此之外,还可以接受集合(非有序)来执行命令,此时score默认都是1,还可以带WEIGHTS + +### ZEANGEBYLEX/ZREVRANGEBYLEX/ZLEXCOUNT/ZREMRANGEBYLEX 系列命令 + +1. 以上所有命令格式雷同,都是处理当有序集合中分数全部相当的情况min和max分别指定是字典字母 +2. `ZEANGEBYLEX sorted_set min max` +3. 比如`ZEANGEBYLEX sorted_set - +`返回所有成员,`[a (t` 返回字典大于等于a并且小于t的所有成员 +4. 逆序/成员个数/移除操作都是类似的 + +### ZPOPMAX/ZPOPMIN 命令 + +1. 弹出分值最高或者最低分值的元素 +2. 返回成员和分值 +3. `ZPOPMAX sorted_set [count]`可以通过 count 来指定最多移除的成员数量,默认为1 +4. Redis5.0 以上版本才支持 + +### BZPOPMIN/BZPOPMAX 命令 + +1. 阻塞式的最小或最大的弹出操作, 可以接受多个集合参数,进行遍历检测 +2. `BZPOPMIN/BZPOPMAX sorted_set [sorted_set ] timeout` +3. timeout 为 0 表示无限阻塞等待 + +## HperLogLog + +### HperLogLog数据结构 + +1. 神奇的HyperLogLog算法 +2. Sketch of the Day: HyperLogLog — Cornerstone of a Big Data Infrastructure +3. 这是一个专门解决大数据计数器消耗太多内存问题的一个概率算法,只需要12k就可以统计2^64个元素 +4. 当然这不是精确统计,存在误差,数据量大的时候误差有的时候是允许的,可容允的 + +### PFADD 命令 + +1. `PFADD hperloglog element [element]` 新增元素 +2. 当新增元素是的统计基数值发生变化就返回1,否则反正0 + +### PFCOUNT 命令 + +1. `PFCOUNT hyperloglog [hyperloglog ...]` 计算集合的近视基数 +2. 当参数为多个时候,计算方式为:首先求多个集合的并集,然后对并集求近视基数 + +### PFMERGE 命令 + +1. `PFMERGE destination hyperloglog [hyperloglog ...]`  对多个hyperloglog集合求并集,然后将结果存在dest中 + +2. PFCOUNT 其实是有调用PFMERGE命令的 + +## 位图 + +### 位图结构 + +1. Redis位图bitmap是由多个二进制位组成的数组,数组中每一位都有与之对应的偏移量(索引) + +2. BITMAP 图 + +|index|0|1|2|3| +|-|-|-|-|-| +|位|1|0|0|1| + +### SETBIT 命令 + +1. `SETBIT bitmap offset value` 设置指定偏移位的值 +2. 返回指定偏移量旧值,默认为0 +3. bitmap默认按照字节扩展 +4. offset只能为正值 + +### GETBIT 命令 + +1. `GETBIT bitmap offset`获取指定位置的值 + +### BITCOUNT 命令 + +1. `BITCOUNT bitmap` 统计位图中1的个数 +2. `BITCOUNT bitmap start end` 返回指定字节范围内1的个数,注意start和end为字节偏移量,并不是位offset, 可以使用负数作为参数 + +### BITPOS 命令 + +1. `BITPOS bitmap value` 查询bitmap中第一个被设置为value值的位置 +2. `BITPOS bitmap value [start end]`  在指定范围内查找,但是返回的offset是基于整个bigmap的偏移 +3. start和end可以为负值 + +### BITOP 命令 + +1. `BITOP OP result_key bitmap [bitmap ...]` 对多个bitmap数组执行op操作,将结果存储在result中 +2. op可以是 AND / OR /XOR / NOT + +### BITFIELD 命令 + +1. `BITFIELD bitmap SET type offset value` 根据位偏移来设置bitmap中值value,其中type是指定value的类型,比如i8:8位有符号,u16:16位无符号等 +2. offset 可以换成 #index, 这样可以以字节位来索引具体位置,然后设置值 +3. 可以同时执行多个set命令 +4. `BITFIELD bitmap GET type offset/#index` 获取对应的值,同样的也可以同时执行多个GET +5. `BITFIELD bitmap INCRYBY type offset/#index increment` 对指定范围值加减操作 +6. `BITFIELD bitmap [OVERFLOW WRAP/SAT/FAIL] INCRYBY type offset/#index increment` 可以用来处理加减法结果溢出的情况,分别为环绕/饱和运算/失败 + +### BITMAP STRING + +1. 可以把二进制数组当作是字符串来操作 +2. GET 命令来获取二进制数组值,返回值为二进制字符串 +3. STRLEN 可以得到二进制字符串的长度 +4. GETRANGE 获取指定范围的二进制字符串 + +## GEO位置服务 + +### GEOADD 命令 + +1. `GEOADD location_set longitude latitude name [longitude latitude name]` 添加一个或者多个位置坐标(经纬度) +2. 当执行的是添加的,那么返回添加的位置个数;如果是更新那么返回0 + +### GEOPOS 命令 + +1. `GEOPOS location_set name [name ...]`  获取指定位置的经纬度 +2. 返回值是数组,其中数组元素为二元数组,第一项为经度,第二项为纬度 + +### GEODIST 命令 + +1. `GEODIST location_set name1 name2` 计算俩个位置的直线距离 +2. 默认单位为米,可以通过`[unit]` 来指定单位m/km/mi英里/ft英寸 + +### GEORADIUS 命令 + +1. `GEORADIUS location_set longitude latitude radius unit`  获取指定位置为中心点,radius半径内所有的地点 +2. `WITHDIST` 加上这个后缀参数,可以返回地点和地点与中心位置的直线距离 +3. `WITHCOORD`  返回地点和地点坐标 +4. `[ASD|DESC]` 对返回的结果排序 +5. `[COUNT n]` 限制返回地点的数量 +6. 可以同时指定多个可选参数 + +### GEORADIUSBYMEMBER 命令 + +1. `longitude latitude` 参数换成 `name`地名 + +### GEOHASH 命令 + +1. 获取指定位置的GEOhash值,GEOhash值是经纬度转换而来,并且可以通过GEohash来计算得到经纬度 +2. 在上面的两个命令中都可以指定`WITHHASH`  来返回GEOHASH 而非 经纬度 + +### GEO数据内部存储结构 + +1. 为有序集合,因此可以使用ZSORTED来操作数据,其中score为Geohash值 + +## Stream流 + diff --git "a/source/_posts/TCP \344\270\211\346\254\241\346\217\241\346\211\213\345\222\214\345\233\233\346\254\241\346\214\245\346\211\213.md" "b/source/_posts/TCP \344\270\211\346\254\241\346\217\241\346\211\213\345\222\214\345\233\233\346\254\241\346\214\245\346\211\213.md" new file mode 100644 index 0000000..e96adf8 --- /dev/null +++ "b/source/_posts/TCP \344\270\211\346\254\241\346\217\241\346\211\213\345\222\214\345\233\233\346\254\241\346\214\245\346\211\213.md" @@ -0,0 +1,75 @@ +--- +title: TCP 三次握手和四次挥手 +date: 2020-01-23 8:12:45 +tags: 网络 +categories: 网络 +--- + +### 首先需要了解的知识 +- TCP的包是没有IP地址的,那是IP层上的事。但是有源端口和目标端口。 +- 一个TCP连接需要四个元组来表示是同一个连接(src_ip, src_port, dst_ip, dst_port)准确说是五元组,还有一个是协议。但因为这里只是说TCP协议,所以,这里我只说四元组。 +- 注意上图中的四个非常重要的东西: + - Sequence Number是包的序号,用来解决网络包乱序(reordering)问题。(seq) + - Acknowledgement Number就是ACK——用于确认收到,用来解决不丢包的问题。 + - Window又叫Advertised-Window,也就是著名的滑动窗口(Sliding Window),用于解决流控的。 + - TCP Flag ,也就是包的类型,主要是用于操控TCP的状态机的。主要有SYN、FIN、ACK等 + +### TCP的状态机 +其实,网络上的传输是没有连接的,包括TCP也是一样的。而TCP所谓的“连接”,其实只不过是在通讯的双方维护一个“连接状态”,让它看上去好像有连接一样。所以,TCP的状态变换是非常重要的。 +如图:(用excel画图的难受,又早不到其他好工具,能推荐个吗?) +![TCP连接、通讯、断开流程图](http://genge.cc/wp-content/uploads/2018/09/tcp连接.png) +图中Client 、Server并不是严格区分的,TCP是全双工的,双方都可以充当Server、Client + +1. 三次握手 + 1. 第一次握手:建立连接。客户端发送连接请求报文段,将SYN位置为1,Sequence Number为x;然后,客户端进入SYN_SEND状态,等待服务器的确认; + 2. 第二次握手:服务器收到SYN报文段。服务器收到客户端的SYN报文段,需要对这个SYN报文段进行确认,设置Acknowledgment Number为x+1(Sequence Number+1);同时,自己自己还要发送SYN请求信息,将SYN位置为1,Sequence Number为y;服务器端将上述所有信息放到一个报文段(即SYN+ACK报文段)中,一并发送给客户端,此时服务器进入SYN_RECV状态; + 3. 第三次握手:客户端收到服务器的SYN+ACK报文段。然后将Acknowledgment Number设置为y+1,向服务器发送ACK报文段,这个报文段发送完毕以后,客户端和服务器端都进入ESTABLISHED状态,完成TCP三次握手。 +完成了三次握手,客户端和服务器端就可以开始传送数据。以上就是TCP三次握手的总体介绍。 + +2. 四次挥手 +当客户端和服务器通过三次握手建立了TCP连接以后,当数据传送完毕,肯定是要断开TCP连接的啊。那对于TCP的断开连接,这里就有了神秘的“四次分手”。 + 1. 第一次分手:主机1(可以使客户端,也可以是服务器端),设置Sequence Number和Acknowledgment Number,向主机2发送一个FIN报文段;此时,主机1进入FIN_WAIT_1状态;这表示主机1(client)没有数据要发送给主机2(server)了 + 2. 第二次分手:主机2收到了主机1发送的FIN报文段,向主机1回一个ACK报文段,Acknowledgment Number为Sequence Number加1;主机1进入FIN_WAIT_2状态;主机2告诉主机1,我“同意”你的关闭请求; + 3. 第三次分手:主机2向主机1发送FIN报文段,请求关闭连接,同时主机2进入LAST_ACK状态; + 4. 第四次分手:主机1收到主机2发送的FIN报文段,向主机2发送ACK报文段,然后主机1进入TIME_WAIT状态;主机2收到主机1的ACK报文段以后,就关闭连接;此时,主机1等待2MSL后依然没有收到回复,则证明Server端已正常关闭,那好,主机1也可以关闭连接了。 + +至此,TCP的四次分手就这么愉快的完成了。当你看到这里,你的脑子里会有很多的疑问,很多的不懂,感觉很凌乱;没事,我们继续总结。 + +3. 三次握手和四次挥手意义 +对于建链接的3次握手,主要是要初始化Sequence Number 的初始值。通信的双方要互相通知对方自己的初始化的Sequence Number(缩写为ISN:Inital Sequence Number)——所以叫SYN,全称Synchronize Sequence Numbers。也就上图中的 x 和 y。这个号要作为以后的数据通信的序号,以保证应用层接收到的数据不会因为网络上的传输的问题而乱序(TCP会用这个序号来拼接数据)。 +对于四次挥手,其实仔细看是2次,因为TCP是全双工的,所以,发送方和接收方都需要Fin和Ack。只不过,有一方是被动的,所以看上去就成了所谓的4次挥手。如果两边同时断连接,那就会就进入到CLOSING状态,然后到达TIME_WAIT状态。 + +**为什么要三次握手** +> 在谢希仁著《计算机网络》第四版中讲“三次握手”的目的是“为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误”。在另一部经典的《计算机网络》一书中讲“三次握手”的目的是为了解决“网络中存在延迟的重复分组”的问题。 + +在谢希仁著《计算机网络》书中同时举了一个例子,如下: +> “已失效的连接请求报文段”的产生在这样一种情况下:client发出的第一个连接请求报文段并没有丢失,而是在某个网络结点长时间的滞留了,以致延误到连接释放以后的某个时间才到达server。本来这是一个早已失效的报文段。但server收到此失效的连接请求报文段后,就误认为是client再次发出的一个新的连接请求。于是就向client发出确认报文段,同意建立连接。假设不采用“三次握手”,那么只要server发出确认,新的连接就建立了。由于现在client并没有发出建立连接的请求,因此不会理睬server的确认,也不会向server发送数据。但server却以为新的运输连接已经建立,并一直等待client发来数据。这样,server的很多资源就白白浪费掉了。采用“三次握手”的办法可以防止上述现象发生。例如刚才那种情况,client不会向server的确认发出确认。server由于收不到确认,就知道client并没有要求建立连接。” +![计算机网络三次握手](http://genge.cc/wp-content/uploads/2018/09/计算机网络三次握手.jpg) + +一句话总结就是:**防止了服务器端的一直等待而浪费资源。** + +**为什么要四次挥手** +TCP协议是一种面向连接的、可靠的、基于字节流的运输层通信协议。TCP是全双工模式,这就意味着,当主机1发出FIN报文段时,只是表示主机1已经没有数据要发送了,主机1告诉主机2,它的数据已经全部发送完毕了;但是,这个时候主机1还是可以接受来自主机2的数据;当主机2返回ACK报文段时,表示它已经知道主机1没有数据发送了,但是主机2还是可以发送数据到主机1的;当主机2也发送了FIN报文段时,这个时候就表示主机2也没有数据要发送了,就会告诉主机1,我也没有数据要发送了,之后彼此就会愉快的中断这次TCP连接。 + +为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSED状态? +虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSED状态了,但是我们必须假象网络是不可靠的,有可能最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。 + +### 状态机状态解释 +- CLOSED: 这个没什么好说的了,表示初始状态,也是最后状态。 +- LISTEN: 这个也是非常容易理解的一个状态,表示服务器端的某个SOCKET处于监听状态,可以接受连接了。 +- SYN_RCVD: 这个状态表示接受到了SYN报文,在正常情况下,这个状态是服务器端的SOCKET在建立TCP连接时的三次握手会话过程中的一个中间状态,很短暂,基本上用netstat你是很难看到这种状态的,除非你特意写了一个客户端测试程序,故意将三次TCP握手过程中最后一个ACK报文不予发送。因此这种状态时,当收到客户端的ACK报文后,它会进入到ESTABLISHED状态。 +- SYN_SENT: 这个状态与SYN_RCVD遥想呼应,当客户端SOCKET执行CONNECT连接时,它首先发送SYN报文,因此也随即它会进入到了SYN_SENT状态,并等待服务端的发送三次握手中的第2个报文。SYN_SENT状态表示客户端已发送SYN报文。 +- ESTABLISHED:这个容易理解了,表示连接已经建立了。 +- FIN_WAIT_1: 这个状态要好好解释一下,其实FIN_WAIT_1和FIN_WAIT_2状态的真正含义都是表示等待对方的FIN报文。而这两种状态的区别是:FIN_WAIT_1状态实际上是当SOCKET在ESTABLISHED状态时,它想主动关闭连接,向对方发送了FIN报文,此时该SOCKET即进入到FIN_WAIT_1状态。而当对方回应ACK报文后,则进入到FIN_WAIT_2状态,当然在实际的正常情况下,无论对方何种情况下,都应该马上回应ACK报文,所以FIN_WAIT_1状态一般是比较难见到的,而FIN_WAIT_2状态还有时常常可以用netstat看到。 +- FIN_WAIT_2:上面已经详细解释了这种状态,实际上FIN_WAIT_2状态下的SOCKET,表示半连接,也即有一方要求close连接,但另外还告诉对方,我暂时还有点数据需要传送给你,稍后再关闭连接。 +- TIME_WAIT: 表示收到了对方的FIN报文,并发送出了ACK报文,就等2MSL后即可回到CLOSED可用状态了。如果FIN_WAIT_1状态下,收到了对方同时带FIN标志和ACK标志的报文时,可以直接进入到TIME_WAIT状态,而无须经过FIN_WAIT_2状态。 +- CLOSING: 这种状态比较特殊,实际情况中应该是很少见,属于一种比较罕见的例外状态。正常情况下,当你发送FIN报文后,按理来说是应该先收到(或同时收到)对方的ACK报文,再收到对方的FIN报文。但是CLOSING状态表示你发送FIN报文后,并没有收到对方的ACK报文,反而却也收到了对方的FIN报文。什么情况下会出现此种情况呢?其实细想一下,也不难得出结论:那就是如果双方几乎在同时close一个SOCKET的话,那么就出现了双方同时发送FIN报文的情况,也即会出现CLOSING状态,表示双方都正在关闭SOCKET连接。 +- CLOSE_WAIT: 这种状态的含义其实是表示在等待关闭。怎么理解呢?当对方close一个SOCKET后发送FIN报文给自己,你系统毫无疑问地会回应一个ACK报文给对方,此时则进入到CLOSE_WAIT状态。接下来呢,实际上你真正需要考虑的事情是察看你是否还有数据发送给对方,如果没有的话,那么你也就可以close这个SOCKET,发送FIN报文给对方,也即关闭连接。所以你在CLOSE_WAIT状态下,需要完成的事情是等待你去关闭连接。 +- LAST_ACK: 这个状态还是比较容易好理解的,它是被动关闭一方在发送FIN报文后,最后等待对方的ACK报文。当收到ACK报文后,也即可以进入到CLOSED可用状态了。 + +### 参考文献 +[TCP 的那些事儿(上) | | 酷 壳 - CoolShell](https://coolshell.cn/articles/11564.html) +[通俗大白话来理解TCP协议的三次握手和四次分手 · Issue #14 · jawil/blog · GitHub](https://github.com/jawil/blog/issues/14) +[面试常考题-TCP三次握手与四次握手 - CSDN博客](https://blog.csdn.net/zxygww/article/details/45009613) +[TCP协议中的三次握手和四次挥手(图解) - CSDN博客](https://blog.csdn.net/whuslei/article/details/6667471) +[Linux Socket 网络编程](http://www.cnblogs.com/MyLove-Summer/p/5215287.html) diff --git "a/source/_posts/linux fork\345\207\275\346\225\260\350\257\246\350\247\243.md" "b/source/_posts/linux fork\345\207\275\346\225\260\350\257\246\350\247\243.md" new file mode 100644 index 0000000..81b20bd --- /dev/null +++ "b/source/_posts/linux fork\345\207\275\346\225\260\350\257\246\350\247\243.md" @@ -0,0 +1,47 @@ +--- +title: linux fork函数详解 +date: 2020-01-23 15:12:45 +tags: linux +categories: C/C++ +toc: true +--- + +### 函数原型: ` pid_t fork(void) ` +1. 参数:不需要参数 +2. 需要的头文件` ` 和 `` +3. 返回值分两种情况: + - 返回0表示成功创建子进程,并且接下来进入子进程执行流程 + - 返回PID(>0),成功创建子进程,并且继续执行父进程流程代码 + - 返回非正数(<0),创建子进程失败,失败原因主要有: + - 进程数超过系统所能创建的上限,errno会被设置为EAGAIN + - 系统内存不足,errno会被设置为ENOMEM + +### 地址空间 +> 使用 fork() 函数得到的子进程是父进程的一个复制品,它从父进程处继承了整个进程的地址空间:包括进程上下文(进程执行活动全过程的静态描述)、进程堆栈、打开的文件描述符、信号控制设定、进程优先级、进程组号等。**子进程所独有的只有它的进程号,计时器等(只有小量信息)**。因此,使用 fork() 函数的代价是很大的。 + +### 共享方式 +> 实际上,更准确来说,Linux 的 fork() 使用是通过**写时拷贝** (copy- on-write) 实现。写时拷贝是一种可以推迟甚至避免拷贝数据的技术。内核此时并不复制整个进程的地址空间,而是让父子进程共享同一个地址空间。只用在需要写入的时候才会复制地址空间,从而使各个进行拥有各自的地址空间。也就是说,资源的复制是在需要写入的时候才会进行,在此之前,只有以只读方式共享。 + +### 执行顺序 +> 创建新进程成功后,系统中出现两个基本完全相同的进程,这两个进程执行没有固定的先后顺序,哪个进程先执行要看系统的进程调度策略。 + +> linux有个类似的函数vfork():函数表面看起来都一样,但是它保证子进程先运行,在它调用 exec(进程替换) 或 exit(退出进程)之后父进程才可能被调度运行。子进程共享父进程的地址空间(准确来说,在调用 exec(进程替换) 或 exit(退出进程) 之前与父进程数据是共享的), vfork() 创建的子进程会执行完后,才到父进程执行。 + +### 区别 +子进程与父进程的区别在于: +1. 除了文件锁以外,其他的锁都会被继承 +2. 各自的进程ID和父进程ID不同 +3. 子进程的未决告警被清除; +4. 子进程的未决信号集设置为空集。 + +### 孤儿进程、僵尸进程 +> fork系统调用之后,父子进程将交替执行,执行顺序不定。如果父进程先退出,子进程还没退出那么子进程的父进程将变为init进程(托孤给了init进程)。(注:任何一个进程都必须有父进程)如果子进程先退出,父进程还没退出,那么子进程必须等到父进程捕获到了子进程的退出状态才真正结束,否则这个时候子进程就成为僵进程(僵尸进程:只保留一些退出信息供父进程查询) + +### 多线程进程的Fork调用 +坑大,面试可能会问道,工作中也要小心使用 +[云风 BLOG: 极不和谐的 fork 多线程程序](https://blog.codingnow.com/2011/01/fork_multi_thread.html) +讲的主要是当前的进程processA (thread a/b/c)的当前子线程thread a调用fork后,会创建子进程,但是只是复制了thread a,总结一句就是所有父进程中别的线程,到了子进程中都是突然蒸发掉的。这样会导致各种死锁问题,以及各种数据不一致等问题。最好的办法是在多线程进程里不是用fork。如果非使用不可,尽量fork完毕后直接exec,不调用任何其他除了fork之外的函数。exec可以覆盖内存空间,可以解决所有关于锁的问题。 +还有一些文章可以看看: +1. [谨防fork与锁之间的深坑 - CSDN博客](https://blog.csdn.net/Move_now/article/details/73537535) +2. [子进程继承父进程中互斥锁的讨论 - CSDN博客](https://blog.csdn.net/lyh__521/article/details/45921515) +3. [在多线程中使用fork函数导致死锁,以及解决方案 - CSDN博客](https://blog.csdn.net/u011878172/article/details/79438584) \ No newline at end of file diff --git "a/source/_posts/linux \345\206\205\346\240\270RingBuffer\345\256\236\347\216\260.md" "b/source/_posts/linux \345\206\205\346\240\270RingBuffer\345\256\236\347\216\260.md" new file mode 100644 index 0000000..0aa77d3 --- /dev/null +++ "b/source/_posts/linux \345\206\205\346\240\270RingBuffer\345\256\236\347\216\260.md" @@ -0,0 +1,303 @@ +--- +title: linux 内核RingBuffer实现 +date: 2020-01-22 15:12:45 +tags: linux +categories: C/C++ +--- + +实现方式非常巧妙,刚开始看的有点奇怪,当发现实现原理后惊讶了一番.. + +```c++ +/**@brief 仿照linux kfifo写的ring buffer +* ring_buffer.h + * */ + +#ifndef KFIFO_HEADER_H +#define KFIFO_HEADER_H + +#include +#include +#include +#include +#include +#include + +//判断x是否是2的次方 +#define is_power_of_2(x) ((x) != 0 && (((x) & ((x) - 1)) == 0)) +//取a和b中最小值 +#define min(a, b) (((a) < (b)) ? (a) : (b)) + +struct ring_buffer +{ + void *buffer; //缓冲区 + uint32_t size; //大小 + uint32_t in; //入口位置 + uint32_t out; //出口位置 + pthread_mutex_t *f_lock; //互斥锁 +}; +//初始化缓冲区 +struct ring_buffer* ring_buffer_init(void *buffer, uint32_t size, pthread_mutex_t *f_lock) +{ + assert(buffer); + struct ring_buffer *ring_buf = NULL; + if (!is_power_of_2(size)) + { + fprintf(stderr,"size must be power of 2.\n"); + return ring_buf; + } + ring_buf = (struct ring_buffer *)malloc(sizeof(struct ring_buffer)); + if (!ring_buf) + { + fprintf(stderr,"Failed to malloc memory,errno:%u,reason:%s", + errno, strerror(errno)); + return ring_buf; + } + memset(ring_buf, 0, sizeof(struct ring_buffer)); + ring_buf->buffer = buffer; + ring_buf->size = size; + ring_buf->in = 0; + ring_buf->out = 0; + ring_buf->f_lock = f_lock; + return ring_buf; +} +//释放缓冲区 +void ring_buffer_free(struct ring_buffer *ring_buf) +{ + if (ring_buf) + { + if (ring_buf->buffer) + { + free(ring_buf->buffer); + ring_buf->buffer = NULL; + } + free(ring_buf); + ring_buf = NULL; + } +} + +//缓冲区的长度 +uint32_t __ring_buffer_len(const struct ring_buffer *ring_buf) +{ + return (ring_buf->in - ring_buf->out); +} + +//从缓冲区中取数据 +uint32_t __ring_buffer_get(struct ring_buffer *ring_buf, void * buffer, uint32_t size) +{ + assert(ring_buf || buffer); + uint32_t len = 0; + size = min(size, ring_buf->in - ring_buf->out); + /* first get the data from fifo->out until the end of the buffer */ + len = min(size, ring_buf->size - (ring_buf->out & (ring_buf->size - 1))); + memcpy(buffer, ring_buf->buffer + (ring_buf->out & (ring_buf->size - 1)), len); + /* then get the rest (if any) from the beginning of the buffer */ + memcpy(buffer + len, ring_buf->buffer, size - len); + ring_buf->out += size; + return size; +} +//向缓冲区中存放数据 +uint32_t __ring_buffer_put(struct ring_buffer *ring_buf, void *buffer, uint32_t size) +{ + assert(ring_buf || buffer); + uint32_t len = 0; + size = min(size, ring_buf->size - ring_buf->in + ring_buf->out); + /* first put the data starting from fifo->in to buffer end */ + len = min(size, ring_buf->size - (ring_buf->in & (ring_buf->size - 1))); + memcpy(ring_buf->buffer + (ring_buf->in & (ring_buf->size - 1)), buffer, len); + /* then put the rest (if any) at the beginning of the buffer */ + memcpy(ring_buf->buffer, buffer + len, size - len); + ring_buf->in += size; + return size; +} + +uint32_t ring_buffer_len(const struct ring_buffer *ring_buf) +{ + uint32_t len = 0; + pthread_mutex_lock(ring_buf->f_lock); + len = __ring_buffer_len(ring_buf); + pthread_mutex_unlock(ring_buf->f_lock); + return len; +} + +uint32_t ring_buffer_get(struct ring_buffer *ring_buf, void *buffer, uint32_t size) +{ + uint32_t ret; + pthread_mutex_lock(ring_buf->f_lock); + ret = __ring_buffer_get(ring_buf, buffer, size); + //buffer中没有数据 + if (ring_buf->in == ring_buf->out) + ring_buf->in = ring_buf->out = 0; + pthread_mutex_unlock(ring_buf->f_lock); + return ret; +} + +uint32_t ring_buffer_put(struct ring_buffer *ring_buf, void *buffer, uint32_t size) +{ + uint32_t ret; + pthread_mutex_lock(ring_buf->f_lock); + ret = __ring_buffer_put(ring_buf, buffer, size); + pthread_mutex_unlock(ring_buf->f_lock); + return ret; +} +#endif + +``` + +使用栗子,采用多线程模拟生产者和消费者编写测试程序,如下所示: +```c +/**@brief ring buffer测试程序,创建两个线程,一个生产者,一个消费者。 + * 生产者每隔1秒向buffer中投入数据,消费者每隔2秒去取数据。 + * */ +#include "ring_buffer.h" +#include +#include + +#define BUFFER_SIZE 1024 * 1024 + +typedef struct student_info +{ + uint64_t stu_id; + uint32_t age; + uint32_t score; +}student_info; + + +void print_student_info(const student_info *stu_info) +{ + assert(stu_info); + printf("id:%lu\t",stu_info->stu_id); + printf("age:%u\t",stu_info->age); + printf("score:%u\n",stu_info->score); +} + +student_info * get_student_info(time_t timer) +{ + student_info *stu_info = (student_info *)malloc(sizeof(student_info)); + if (!stu_info) + { + fprintf(stderr, "Failed to malloc memory.\n"); + return NULL; + } + srand(timer); + stu_info->stu_id = 10000 + rand() % 9999; + stu_info->age = rand() % 30; + stu_info->score = rand() % 101; + print_student_info(stu_info); + return stu_info; +} + +void * consumer_proc(void *arg) +{ + struct ring_buffer *ring_buf = (struct ring_buffer *)arg; + student_info stu_info; + while(1) + { + sleep(2); + printf("------------------------------------------\n"); + printf("get a student info from ring buffer.\n"); + ring_buffer_get(ring_buf, (void *)&stu_info, sizeof(student_info)); + printf("ring buffer length: %u\n", ring_buffer_len(ring_buf)); + print_student_info(&stu_info); + printf("------------------------------------------\n"); + } + return (void *)ring_buf; +} + +void * producer_proc(void *arg) +{ + time_t cur_time; + struct ring_buffer *ring_buf = (struct ring_buffer *)arg; + while(1) + { + time(&cur_time); + srand(cur_time); + int seed = rand() % 11111; + printf("******************************************\n"); + student_info *stu_info = get_student_info(cur_time + seed); + printf("put a student info to ring buffer.\n"); + ring_buffer_put(ring_buf, (void *)stu_info, sizeof(student_info)); + printf("ring buffer length: %u\n", ring_buffer_len(ring_buf)); + printf("******************************************\n"); + sleep(1); + } + return (void *)ring_buf; +} + +int consumer_thread(void *arg) +{ + int err; + pthread_t tid; + err = pthread_create(&tid, NULL, consumer_proc, arg); + if (err != 0) + { + fprintf(stderr, "Failed to create consumer thread.errno:%u, reason:%s\n", + errno, strerror(errno)); + return -1; + } + return tid; +} +int producer_thread(void *arg) +{ + int err; + pthread_t tid; + err = pthread_create(&tid, NULL, producer_proc, arg); + if (err != 0) + { + fprintf(stderr, "Failed to create consumer thread.errno:%u, reason:%s\n", + errno, strerror(errno)); + return -1; + } + return tid; +} + + +int main() +{ + void * buffer = NULL; + uint32_t size = 0; + struct ring_buffer *ring_buf = NULL; + pthread_t consume_pid, produce_pid; + + pthread_mutex_t *f_lock = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t)); + if (pthread_mutex_init(f_lock, NULL) != 0) + { + fprintf(stderr, "Failed init mutex,errno:%u,reason:%s\n", + errno, strerror(errno)); + return -1; + } + buffer = (void *)malloc(BUFFER_SIZE); + if (!buffer) + { + fprintf(stderr, "Failed to malloc memory.\n"); + return -1; + } + size = BUFFER_SIZE; + ring_buf = ring_buffer_init(buffer, size, f_lock); + if (!ring_buf) + { + fprintf(stderr, "Failed to init ring buffer.\n"); + return -1; + } +#if 0 + student_info *stu_info = get_student_info(638946124); + ring_buffer_put(ring_buf, (void *)stu_info, sizeof(student_info)); + stu_info = get_student_info(976686464); + ring_buffer_put(ring_buf, (void *)stu_info, sizeof(student_info)); + ring_buffer_get(ring_buf, (void *)stu_info, sizeof(student_info)); + print_student_info(stu_info); +#endif + printf("multi thread test.......\n"); + produce_pid = producer_thread((void*)ring_buf); + consume_pid = consumer_thread((void*)ring_buf); + pthread_join(produce_pid, NULL); + pthread_join(consume_pid, NULL); + ring_buffer_free(ring_buf); + free(f_lock); + return 0; +} + +``` + +## 参考文献 +[linux内核数据结构之kfifo - Daleshi的技术随笔 - 博客园](https://www.cnblogs.com/Anker/p/3481373.html) + diff --git "a/source/_posts/linux \345\256\211\350\243\205\346\214\207\345\256\232\347\211\210\346\234\254MySql.md" "b/source/_posts/linux \345\256\211\350\243\205\346\214\207\345\256\232\347\211\210\346\234\254MySql.md" new file mode 100644 index 0000000..0d5ce95 --- /dev/null +++ "b/source/_posts/linux \345\256\211\350\243\205\346\214\207\345\256\232\347\211\210\346\234\254MySql.md" @@ -0,0 +1,79 @@ +--- +title: linux 安装指定版本MySql +date: 2020-01-23 15:12:39 +tags: linux +categories: Mysql +--- + +由于工作环境、生产环境,我们使用的操作系统为为CentOS6.9,所需mysql版本为5.7,目前CentOS6.x系统默认mysql版本为5.1,这个版本是实在是太旧了。 + +### 彻底卸载系统已经安装的旧版本 +- 检查系统已经安装的mysql +` rpm -qa|grep -i mysql` + +- 删除包 +`rpm -ev mysql_lib_xxxx` + +- 删除老版本安装残留文件 +`find / -iname mysql*` 删除对应目录已经文件 + +- 删除my.cnf配置文件 + +### 使用yum安装MySql5.7 + +- 下载mysql5.7源 +`wget dev.mysql.com/get/mysql-community-release-el6-5.noarch.rpm` + +- 安装源 +`yum localinstall mysql-community-release-el6-5.noarch.rpm` + +- 查看可用源中包含哪些版本并开启指定版本 +``` +yum repolist all | grep mysql +yum-config-manager --disable mysql56-community +yum-config-manager --disable mysql55-community +yum-config-manager --enable mysql57-community +``` + +- yum安装mysql +`yum install mysql-community-server` + +- 启动mysql +`service mysqld start` + +- 设置开机自动启动 + +``` +chkconfig --list | grep mysqld +chkconfig mysqld on +``` + +- 安装设置命令 +`mysql_secure_installation` + +### 修改root密码 +因为刚才启动的时候是系统默认配置的临时密码 +使用如下命令可以查看,并且修改: +``` +sudo grep 'temporary password' /var/log/mysqld.log +mysql -u root -p +ALTER USER 'root'@'localhost' IDENTIFIED BY 'newPassword'; +``` + +### 设置允许连接数据库 +命令如下: +``` +mysql -u root -p +grant all privileges on *.* to root@"%" identified by 'passwordith grant option;   +flush privileges; +``` + +### 遇到的问题 +- 比较奇怪,域名解析错误 + 在yum install 的时候,发现大量的[Errno 14] PYCURL ERROR 6 - "Couldn't resolve host 'mirrors.aliyun.com'"错误,开始以为自己源设置错误,后来才知道,机器卡死过一次,导致系统莫名其妙的错误,了解网络的很快就知道需要设置系统的DNS + 在`/etc/resolv.conf`文件中加入如下内容: + ``` + nameserver 8.8.8.8 + nameserver 114.114.114.114 + ``` + 之前是啥也没有的,都是些没用的注释解释信息 \ No newline at end of file diff --git "a/source/_posts/linux \347\263\273\347\273\237\344\277\241\345\217\267\345\222\214\344\270\255\346\226\255\345\270\270\350\257\206.md" "b/source/_posts/linux \347\263\273\347\273\237\344\277\241\345\217\267\345\222\214\344\270\255\346\226\255\345\270\270\350\257\206.md" new file mode 100644 index 0000000..39b416e --- /dev/null +++ "b/source/_posts/linux \347\263\273\347\273\237\344\277\241\345\217\267\345\222\214\344\270\255\346\226\255\345\270\270\350\257\206.md" @@ -0,0 +1,138 @@ +--- +title: linux 系统信号和中断常识 +date: 2020-01-23 15:12:40 +tags: linux +categories: Linux +--- +### 什么是中断 +1. 中断基本概念 +> 中断是指计算机在执行期间,系统内发生任何非寻常的或非预期的急需处理事件,使得CPU暂时中断当前正在执行的程序而转去执行相应的事件处理程序,待处理完毕后又返回原来被中断处继续执行或调度新的进程执行的过程。引起中断发生的事件被称为中断源。中断源向CPU发出的请求中断处理信号称为中断请求,而CPU收到中断请求后转到相应的事件处理程序称为中断响应。 +在有些情况下,尽管产生了中断源和发出了中断请求,但CPU内部的处理器状态、字PSW的中断允许位已被清除,从而不允许CPU响应中断。这种情况称为禁止中断。CPU禁止中断后只有等到PSW的中断允许位被重新设置后才能接收中断。禁止中断也称为关中断,PSW的中断允许位的设置也被称为开中断。开中断和关中断是为了保证某段程序执行的原子性。 +还有一个比较常用的概念是中断屏蔽。中断屏蔽是指在中断请求产生之后,系统有选择地封锁一部分中断而允许另一部分中断仍能得到响应。不过,有些中断请求是不能屏蔽甚至不能禁止的,也就是说,这些中断具有最高优先级,只要这些中断请求一旦提出,CPU必须立即响应。例如,电源掉电事件所引起的中断就是不可禁止和不可屏蔽的。 + +2. 中断分类与等级 +> 根据系统对中断处理的需要,操作系统一般对中断进行分类并对不同的中断赋予不同的处理优先级,以便在不同的中断同时发生时,按轻重缓急进行处理。 +根据中断源产生的条件,可把中断分为外中断和内中断。外中断是指来自处理器和内存外部的中断,包括I/0设备发出的I/O中断、外部信号中断(例如用户键人ESC键)。各种定时器引起的时钟中断以及调试程序中设置的断点等引起的调试中断等。外中断在狭义上一般被称为中断。 +内中断主要指在处理器和内存内部产生的中断。内中断一般称为陷阱(trap)或异常。它包括程序运算引起的各种错误,如地址非法、校验错、页面失效、存取访问控制错、算术操作溢出、数据格式非法、除数为零、非法指令、用户程序执行特权指令、分时系统中的时间片中断以及从用户态到核心态的切换等都是陷阱的例子。 +为了按中断源的轻重缓急处理响应中断,操作系统为不同的中断赋予不同的优先级。例如在UNIX系统中,外中断和陷阱的优先级共分为8级。为了禁止中断或屏蔽中断,CPU的处理器状态字PSW中也设有相应的优先级。如果中断源的优先级高于PSW的优先级,则CPU响应该中断源的请求;反之,CPU屏蔽该中断源的中断请求。 +各中断源的优先级在系统设计时给定,在系统运行时是固定的。而处理器的优先级则根据执行情况由系统程序动态设定。 +除了在优先级的设置方面有区别之外,中断和陷阱还有如下主要区别: +陷阱通常由处理器正在执行的现行指令引起,而中断则是由与现行指令无关的中断源引起的。陷阱处理程序提供的服务为当前进程所用,而中断处理程序提供的服务则不是为了当前进程的。 +CPU执行完一条指令之后,下一条指令开始之前响应中断,而在一条指令执行中也可以响应陷阱。例如执行指令非法时,尽管被执行的非法指令不能执行结束,但CPU仍可对其进行处理。 + +3. 软中断 +> 软中断的概念主要来源于UNIX系统。软中断是对应于硬中断而言的。通过硬件产生相应的中断请求,称为硬中断。而软中断则不然,它是在通信进程之间通过模拟硬中断而实现的一种通信方式。中断源发出软中断信号后,CPU或者接收进程在“适当的时机”进行中断处理或者完成软中断信号所对应的功能。这里“适当的时机”,表示接收软中断信号的进程须等到该接收进程得到处理器之后才能进行。如果该接收进程是占据处理器的,那么,该接收进程在接收到软中断信号后将立即转去执行该软中断信号所对应的功能。 + +4. 中断处理过程 +一旦CPU响应中断,转人中断处理程序,系统就开始进行中断处理。下面对中断处理过程进行详细说明: + + 1. CPU检查响应中断的条件是否满足。CPU响应中断的条件是:有来自于中断源的中断请求、CPU允许中断。如果中断响应条件不满足,则中断处理无法进行。 + + 2. 如果CPU响应中断,则CPU关中断,使其进入不可再次响应中断的状态。 + + 3. 保存被中断进程现场。为了在中断处理结束后能使进程正确地返回到中断点,系统必须保存当前处理器状态字PSW和程序计数器PC等的值。这些值一般保存在特定堆栈或硬件寄存器中。 + + 4. 分析中断原因,调用中断处理子程序。在多个中断请求同时发生时,处理优先级最高的中断源发出的中断请求。在系统中,为了处理上的方便,通常都是针对不同的中断源编制有不同的中断处理子程序(陷阱处理子程序)。这些子程序的人口地址(或陷阱指令的人口地址)存放在内存的特定单元中。再者,不同的中断源也对应着不同的处理器状态字PSW。这些不同的PSW被放在相应的内存单元中,与中断处理子程序人口地址一起构成中断向量。显然,根据中断或陷阱的种类,系统可由中断向量表迅速地找到该中断响应的优先级、中断处理子程序(或陷阱指令)的入口地址和对应的PSW。 + + 5. 执行中断处理子程序。对陷阱来说,在有些系统中则是通过陷阱指令向当前执行进程发出软中断信号后调用对应的处理子程序执行。 + + 6. 退出中断,恢复被中断进程的现场或调度新进程占据处理器。 + + 7. 开中断,CPU继续执行。 + +5. 设备管理程序与中断方式 +> 处理器的高速和输入输出设备低速之间的矛盾,是设备管理要解决的一个重要问题。为了提高整体效率,减少在程序直接控制方式中的CPU等待时间以及提高系统的并行工作效率,采用中断方式来控制输入输出设备和内存与CPU之间的数据传送,是很有必要的。在硬件结构上,这种方式要求CPU与输入输出设备(或控制器)之间有相应的中断请求线,而且在输入输出设备控制器的控制状态寄存器上有相应的中断允许位。 + + +### 信号 +信号驱动的异步I/O是指一旦设备准备好,就主动通知应用程序,这种情况下应用程序就不需要查询设备状态。异步 I/O 和硬件上常提的中断的概念类似,信号是在软件层次上对中断机制的一种模拟。 + +1. 信号通信机制 +软中断信号(signal,又简称为信号)是进程间通信机制中唯一的异步通信机制,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号到底什么时候到达。 +进程之间可以互相通过系统调用kill发送软中断信号,内核也可以因为内部事件而给进程发送信号,通知进程发生了某个事件。信号机制除了基本通知功能外,还可以传递附加信息。 + +2. 处理信号 +收到信号的进程对各种信号有不同的处理方法,主要分为以下三类: + - 类似中断的处理程序,对于需要处理的信号,进程可以指定处理函数,由该函数来处理。进程通过系统调用signal来指定进程对某个信号的处理行为。 + - 忽略某个信号,对该信号不做任何处理,就象未发生过一样。 + - 对该信号的处理保留系统的默认值,对大部分的信号的缺省操作是使得进程终止。 + +需要注意的是,信号处理函数注册后,当信号来临并被触发调用信号处理函数,当同样类型信号再次到来时,并不会执行信号处理函数,而是使用信号的系统默认处理方式,大部分都是使得进程终止。 + + +3. 信号可靠性 +从可靠性方面信号分为可靠信号与不可靠信号;信号的可靠与不可靠只与信号值有关,与信号的发送及安装函数无关。信号值位于 SIGRTMIN 和 SIGRTMAX 之间的信号都是可靠信号,可靠信号克服了信号可能丢失的问题。 +非实时信号都不支持排队,都是不可靠信号;实时信号都支持排队,都是可靠信号。 +不可靠信号就是指发送的信号内核不一定能够发送给 目标进程,信号可能丢失。 不可靠信号在内核中存储的方式是位图和链表,当内核接收一个信号后,先判断它是否 已经存在,不存在就把他对应的位置一,并将信号挂入链表,存在则丢弃信号。 可靠信号则是把信号放入队列,再链入链表,所以也就保证了信号不丢失。 +非可靠信号一般都有确定的用途及含义, 可靠信号则可以让用户自定义使用 +需要注意的是:这里的“实时”和实时操作系统中的“实时”没有任何联系,实时信号在处理速度上并不会比普通信号快,它们之间的区别就是:普通信号会对多次的同一个信号进行“合并”处理,而实时信号会一一处理。这就要求我们在编写信号监听函数时,要捕获普通信号,必须时刻轮训监听,因为系统默认会丢弃同种类型的普通信号! + +4. 信号传递顺序 +如果存在多个未决信号,同一个未决信号会按照发送顺序来递送信号,不同的未决信 号按照信号的序号大小来递送,序号小的信号会先被递送到进程。另外,linux中会优 先递送不可靠信号 +Linux中的信号机制优先级是:高优先级中断->低优先级中断->软中断->信号->进程运行。 +需要注意的是,在用户态不存在未决信号。信号处理一般发生在进程从内核态返回用户态的时候。内核空间没有信号处理机制,内核态也不会处理信号否者信号拥有系统最高权限,变得不再安全 + +5. 多线程中信号造成死锁 +如果一个线程持有锁,在操作临界区内容时,被信号中断了,转而去执行信号处理函数, 而信号处理函数再次对临界区加锁就会造成死锁。 +解决的方法就是使用信号等待函数,线程阻塞等待信号处理函数直到处理完毕,也就是 所说的化异步为同步。 + +6. 信号响应过程 + 1) A进程调用信号发送函数,发送信号给B,这是软中断,所以A进程会进入内核态运行操作系统的信号调度代码 + 2) 操作系统发现B进程正在运行,于是写入管理B进程的某个数据结构 + 3) 操作系统返回给A,A继续执行 + 4) B进程分配的处理器时间用完了,被时钟硬件中断 + 5) 操作系统的时钟硬件中断处理函数准备挂起B进程,也就是把寄存器和函数堆栈保存起来,发现B进程收到了singal + 6) 操作系统在保存好B进程的stack和register后,新开stack(为了不干扰B进程真正的代码stack),激活B进程,B进程的信号处理函数。 + +7. 信号生命周期(和响应过程类似) + - 在目的进程中安装该信号。即设置捕获该信号时进程执行的操作,采用signal 或者 sigaction 系统调用来实现。 + - 信号被某个进程产生,同时设置该信号的目的进程(使用pid),之后交给操作系统进行管理。采用kill()、arise()、alarm()等系统调用来实现。 + - 信号在目的进程被注册。就是把信号值加入到进程的PCB(task_struct)中相关的数据结构里——未决信号的数据成员,信号携带的其他信息被保留到未决信的队列的某个sigqueue结构中。 + - 信号在进程中注销。在执行信号处理函数前,要把信号在进程中注销。 + - 信号生命的终结。进程终止当前的工作,保护上下文,执行信号处理函数,之后恢复。 + +8. 信号阻塞集(屏蔽集、掩码) +信号阻塞集也称信号屏蔽集、信号掩码。每个进程都有一个阻塞集,创建子进程时子进程将继承父进程的阻塞集。信号阻塞集用来描述哪些信号递送到该进程的时候被阻塞(在信号发生时记住它,直到进程准备好时再将信号通知进程)。 +所谓阻塞并不是禁止传送信号, 而是暂缓信号的传送。若将被阻塞的信号从信号阻塞集中删除,且对应的信号在被阻塞时发生了,进程将会收到相应的信号。 + +### 信号实现机制 +1. 发送信号 +内核给一个进程发送软中断信号的方法,是在进程所在的进程表项的信号域设置对应于该信号的位。如果信号发送给一个正在睡眠的进程,那么要看该进程进入睡眠的优先级,如果进程睡眠在可被中断的优先级上,则唤醒进程;否则仅设置进程表中信号域相应的位,而不唤醒进程。 +进程的 PCB 中有关于本进程中未决信号的数据成员` struct sigpending pending`: +``` +struct sigpending{ + struct sigqueue *head, *tail; + sigset_t signal; +}; + +``` + +第三个成员是进程中所有未决信号集,第一、第二个成员分别指向一个sigqueue类型的结构链(称之为"未决信号信息链")的首尾,信息链中的每个sigqueue结构刻画一个特定信号所携带的信息,并指向下一个sigqueue结构: +``` +struct sigqueue{ + struct sigqueue *next; + siginfo_t info; +} +``` + +信号在进程中注册指的就是信号值加入到进程的未决信号集sigset_t signal(每个信号占用一位)中,并且信号所携带的信息被保留到未决信号信息链的某个sigqueue结构中。只要信号在进程的未决信号集中,表明进程已经知道这些信号的存在,但还没来得及处理,或者该进程被信号阻塞。 + +当一个可靠信号发送给一个进程时,不管该信号是否已经在进程中注册,都会被再注册一次,因此,信号不会丢失。这意味着同一个可靠信号可以在同一个进程的未决信号信息链中占有多个sigqueue结构(进程每收到一个可靠信号,都会为它分配一个结构来注册该信号信息,并把该结构添加在未决信号链尾)。 + +当一个非可靠信号发送给一个进程时,如果该信号已经在进程中注册(通过sigset_t signal指示),则该信号将被丢弃,造成信号丢失。这意味着同一个非实时信号在进程的未决信号信息链中,至多占有一个sigqueue结构。 + +总之信号注册与否,与发送信号的函数(如kill()或sigqueue()等)以及信号安装函数(signal()及sigaction())无关,只与信号值有关(信号值小于SIGRTMIN的信号最多只注册一次,信号值在SIGRTMIN及SIGRTMAX之间的信号,只要被进程接收到就被注册)。 + +2. 处理信号 +内核处理一个进程收到的信号的时机是在一个进程从内核态返回用户态时。所以,当一个进程在内核态下运行时,软中断信号并不立即起作用,要等到将返回用户态时才处理。进程只有处理完信号才会返回用户态,进程在用户态下不会有未处理完的信号。 +内核处理一个进程收到的软中断信号是在该进程的上下文中,因此,进程必须处于运行状态。当进程接收到一个它忽略的信号时,进程丢弃该信号,就像没有收到该信号似的继续运行。 +如果进程收到一个要捕捉的信号,那么进程从内核态返回用户态时执行用户定义的函数。**而且执行用户定义的函数的方法很巧妙,内核在用户栈上创建一个新的层,该层中将返回地址的值设置成用户定义的处理函数的地址,这样进程从内核返回弹出栈顶时就返回到用户定义的函数处,从函数返回再弹出栈顶时,才返回原先进入内核的地方。**这样做的原因是用户定义的处理函数不能且不允许在内核态下执行(如果用户定义的函数在内核态下运行的话,用户就可以获得任何权限)。 +对于非可靠信号来说,由于在未决信号信息链中最多只占用一个sigqueue结构,因此该结构被释放后,应该把信号在进程未决信号集中删除(信号注销完毕);而对于实时信号来说,可能在未决信号信息链中占用多个sigqueue结构,因此应该针对占用sigqueue结构的数目区别对待:如果只占用一个sigqueue结构(进程只收到该信号一次),则执行完相应的处理函数后应该把信号在进程的未决信号集中删除(信号注销完毕)。否则待该信号的所有sigqueue处理完毕后再在进程的未决信号集中删除该信号。 +当所有未被屏蔽的信号都处理完毕后,即可返回用户空间。对于被屏蔽的信号,当取消屏蔽后,在返回到用户空间时会再次执行上述检查处理的一套流程。 + +### 参考文献 +[CS_Offer/Signal.md at master · xuelangZF/CS_Offer · GitHub](https://github.com/xuelangZF/CS_Offer/blob/master/Linux_OS/Signal.md) +[信号处理的时机](http://lzz5235.github.io/2015/06/04/signal.html) +[面试中关于Linux的信号常问的问题 | 等英博客](https://www.waitig.com/%E9%9D%A2%E8%AF%95%E4%B8%AD%E5%85%B3%E4%BA%8Elinux%E7%9A%84%E4%BF%A1%E5%8F%B7%E5%B8%B8%E9%97%AE%E7%9A%84%E9%97%AE%E9%A2%98.html) +[linux系统编程之信号(一):中断与信号 - mickole - 博客园](http://www.cnblogs.com/mickole/p/3189156.html) +[Linux系统编程——进程间通信:信号中断处理 - CSDN博客](https://blog.csdn.net/tennysonsky/article/details/46010303) \ No newline at end of file diff --git "a/source/_posts/\345\205\250\345\261\200\345\224\257\344\270\200ID\347\224\237\346\210\220\347\256\227\346\263\225\344\274\230\345\214\226.md" "b/source/_posts/\345\205\250\345\261\200\345\224\257\344\270\200ID\347\224\237\346\210\220\347\256\227\346\263\225\344\274\230\345\214\226.md" new file mode 100644 index 0000000..7829228 --- /dev/null +++ "b/source/_posts/\345\205\250\345\261\200\345\224\257\344\270\200ID\347\224\237\346\210\220\347\256\227\346\263\225\344\274\230\345\214\226.md" @@ -0,0 +1,596 @@ +--- +title: 全局唯一ID生成算法优化 +date: 2020-01-23 15:12:42 +tags: uuid +categories: C/C++ +--- + + + +- [前言](#前言) +- [Snowflake算法介绍](#Snowflake算法介绍) +- [SnowFlake算法优化](#SnowFlake算法优化) + * [目前算法设计缺陷](#目前算法设计缺陷) + * [设计优化](#设计优化) +- [是否这样就完美了呢?](#是否这样就完美了呢?) +- [Talk is cheap, show you the code](#Talk-is-cheap-show-you-the-code) +- [参考文章](#参考文章) + + + +## 前言 + +在进程启动前,一般会给每个进程静态分配一个唯一标识ID(ServerID或者PipeID) +``` +struct SServerID +{ + UINT16 wPlatform = 0; ///< Platform 平台id + UINT16 wArea = 0; ///< Area 区服id + UINT16 wType = 0; ///< Type 服务器App类型 + UINT16 wIndex = 0; ///< Index 服务器App编号索引 +}; +``` + +每个Role/Item/Hero/Mail 等创建的时候都会创建一个UUID来唯一标识,其中Mail_Uuid还有趋势递增的需求 + +## Snowflake算法介绍 + +*SnowFlake分布式生成Id算法由Twitter开源* + +SnowFlake算法生成id的结果是一个64bit大小的UINT64整数,它的结构如下图: +![snowflake uuid-64bit](:storage\1a51b319-5d9b-40f9-a828-cfc88309cea2\4dbfceed.png) + +SnowFlake的优点: + - 整体上按照时间自增排序 + - 整个分布式系统内不会产生ID碰撞(时间戳和自增序列以外的字段作区分), 并且效率较高(位运算), + +SnowFlake每秒能够产生6.4万ID左右.(和5段位的配置位数有关) +UUID从高位到低位依次排列: +- 第一段:39位, 当对于某一个时间点的时间戳差值(至少10年可用) +- 第二段:3 位, 平台platform或大区id,比如QQ Android/QQ IOS/Wechat IOS 等(8) +- 第三段:11位, 区服area id,对应的就是服务器小区的ID(2048) +- 第四段:5 位, 服务器APP实例id index(32), Notice:appid需要在area范围内唯一 +- 第五段:6 位, 自增长id,也就是说在一个完整的毫秒时间内最多可以生成64个UUID + +## SnowFlake算法优化 + +### 目前算法设计缺陷 +在实际服务器运行过程中,尤其在游戏服务器开发期间,大量用户注册,会有瞬间生成大量UUID的需求 + +之前服务器的做法是: +```cpp +//遍历了一圈64个,等下一个毫秒生成 +if (m_nGlobalSeq == m_nMliSeq) +{ + // 毫秒内序列溢出, 阻塞到下一个毫秒,获得新的时间戳 + nCurTimestamp = WaitForNextMilli(m_nLastTimestamp); +} + +/** +* 阻塞到下一个毫秒,直到获得新的时间戳 +* +* @param lastTimestamp 上次生成ID的时间截 +* @return 当前时间戳 +*/ +UINT64 WaitForNextMilli(UINT64 lastTimestamp) const +{ + UINT64 nTimestamp = _GetNowMliTime(); + while (nTimestamp <= lastTimestamp) + { + nTimestamp = _GetNowMliTime(); + } + return nTimestamp; +} + +``` +这里使用while强行等待到下一毫秒,相当于阻塞当前线程,从而成为热点函数,需要去优化 + +### 设计优化 +- 合理分配各字段占用bit(图中已经是调整过的) +- 将生成uuid单独出一个独立全局Server组件,提供全局唯一UUID服务,这样除去高位39bit外理论上其他bit都可以当作自增位,而且可以提前生成很多uuid放在pool当中,有需要的进程从当中取,可以满足需求,Baidu在github上开源的UUID生成算法就是这样处理的 +- 我们取一个折中方案,只是在server中加一个Pool,存放提前生成好的UUIDs,每次业务端需要UUID的时候首先从Pool中取,如果取不到就走原来的流程 + +```cpp +UINT64 GenId() +{ + if (!IsEmpty()) + { + //从Pool中取 + return PopFrontElement(); + } + + return NextId(); +} +``` + +既然存在Pool,那么就需要设计Pool中元素填充方案 +首先将Pool设定一个合适的固定最大值 const UINT32 UUID_POOL_MAX_SIZE = 1 << 13; // uuid pool 最大数 8192 +然后根据当前Pool的状态,来定时填充,每次填充的数量为当前毫秒内所有可以生成的UUID数 +![Fill_Pool.png](:storage\1a51b319-5d9b-40f9-a828-cfc88309cea2\928daf4a.png) + + +```cpp +#include "uuid_pool_mgr.h" +#include "uuid_generator.h" + + +bool CUUIDPool::Init() +{ + FillUuidPool(); + return true; +} + +void CUUIDPool::OnTimer(unsigned int nId) +{ + EXLOG_DEBUG << "[RyzUuid]CUUIDPool::OnTimer Old EUuidPoolState : " << nId; + CUUIDMaker::Instance()->FillPoolWithInMli(); + FillUuidPool(); +} + +void CUUIDPool::FillUuidPool() +{ + EUuidPoolState eUuidDequeState = CUUIDMaker::Instance()->GetUuidDequeState(); + uint32 nUpdateInterval = 5 * 60 * 1000; + switch (eUuidDequeState) + { + case EUuidPoolState_Empty: + nUpdateInterval = 1 * 60 * 1000; + break; + case EUuidPoolState_Not_Full_0_30_Per: + nUpdateInterval = 2 * 60 * 1000; + break; + case EUuidPoolState_Not_Full_30_70_Per: + nUpdateInterval = 3 * 60 * 1000; + break; + case EUuidPoolState_Not_Full_70_100_Per: + nUpdateInterval = 4 * 60 * 1000; + break; + case EUuidPoolState_Full: + nUpdateInterval = 5 * 60 * 1000; + break; + default: + nUpdateInterval = 5 * 60 * 1000; + break; + } + + EXLOG_DEBUG << "[RyzUuid]CUUIDPool::OnTimer Now EUuidPoolState : " << eUuidDequeState; + SetTimer(eUuidDequeState, nUpdateInterval, nUpdateInterval, ETIMER_ONCE); +} + + +enum EUuidPoolState +{ + EUuidPoolState_Empty = 1, + EUuidPoolState_Not_Full_0_30_Per = 2, + EUuidPoolState_Not_Full_30_70_Per = 3, + EUuidPoolState_Not_Full_70_100_Per = 4, + EUuidPoolState_Full = 5, +}; + +//uuid_generator.h +//将当前毫秒内的UUID全部生成并存到Pool中 +void FillPoolWithInMli() +{ + EUuidPoolState nCurState = GetUuidDequeState(); + AtomicUInt64 nCurTimestamp = _GetNowMliTime(); + while (nCurState != EUuidPoolState_Full) + { + UINT64 nNextUUId = NextId(false); + if (nNextUUId == 0) + { + break; + } + + PushBackElement(nNextUUId); + + if (nCurTimestamp != _GetNowMliTime()) + { + break; + } + } +} + +``` + +## 是否这样就完美了呢? + +并没有呢!!! +这个算法强制依赖时间递增,如果时间回拨怎么办? +目前的做法是直接throw new exception +分析时间回拨产生原因 +第一:人物操作,在真实环境一般不会有那个傻逼干这种事情,所以基本可以排除。 +第二:由于有些业务等需要,机器需要同步时间服务器(在这个过程中可能会存在时间回拨,查了下我们服务器一般在10ms以内(2小时同步一次))。 Ntp过程可能产生时间回拨。 +第三:QA和策划测试过程中有需求怎么办? +解决办法: +1. 将uuid_generation独立出来给其他server提供服务 +2. 当回拨时间小于XXms,就等时间追上来之后继续生成。 (XXms对业务没有什么影响) +3. 当时间大于XXms时间我们通过更换AppId位来来解决回拨问题。 + +## Talk is cheap, show you the code +```cpp +#ifndef __UUID_GENERATOR_H__ +#define __UUID_GENERATOR_H__ + +#include "gnsingleton.h" +#include "gntype.h" +#include "gnpipe.h" +#include "gntime.h" +#include "noncopy.h" +#include "gnserverid.h" + +#include +#include +#include +#include +#include +#include +#include + +enum EUuidPoolState +{ + EUuidPoolState_Empty = 1, + EUuidPoolState_Not_Full_0_30_Per = 2, + EUuidPoolState_Not_Full_30_70_Per = 3, + EUuidPoolState_Not_Full_70_100_Per = 4, + EUuidPoolState_Full = 5, +}; + +//#define SNOWFLAKE_ID_MAKER_NO_LOCK +class CSnowflakeIdMaker : private CNoncopy +{ +public: +#ifdef SNOWFLAKE_ID_MAKER_NO_LOCK + typedef std::atomic AtomicUInt; + typedef std::atomic AtomicUInt64; +#else + typedef UINT32 AtomicUInt; + typedef UINT64 AtomicUInt64; +#endif + + const UINT32 UUID_POOL_MAX_SIZE = 1 << 13; // uuid pool 最大数 8192 + const UINT64 START_EPOCH = 1541001600000LL; //开始时间截 (2018-11-01 00:00:00.000),修改此时间可调整可用时长 + + const UINT32 A_TIMESTAMP_BITS = 39; //时间戳所占的位数 + const UINT32 B_PLATFORM_BITS = 3; //平台id所占的位数 + const UINT32 C_AREA_BITS = 11; //区服id所占的位 + const UINT32 D_APP_ID_BITS = 5; //app id所占的位数 + const UINT32 E_INCR_SEQUENCE_BITS = 6; //自增序列所占的位数 + + const UINT32 APP_ID_SHIFT = E_INCR_SEQUENCE_BITS; //APPID向左移位数 + const UINT32 AREA_ID_SHIFT = E_INCR_SEQUENCE_BITS + D_APP_ID_BITS; //小区id向左移位数 + const UINT32 PLATFORM_ID_SHIFT = E_INCR_SEQUENCE_BITS + D_APP_ID_BITS + C_AREA_BITS; //大区id向左移位数 + const UINT32 TIME_STAMP_SHIFT = E_INCR_SEQUENCE_BITS + D_APP_ID_BITS + C_AREA_BITS + B_PLATFORM_BITS; //时间戳向左移位数 + const UINT32 SEQUENCE_MASK = (1 << E_INCR_SEQUENCE_BITS) - 1; //生成序列的掩码 + + + CSnowflakeIdMaker() : m_nPlatformId(0), m_nAreaId(0), m_nGlobalSeq(0), m_nLastTimestamp(0) {} + + CSnowflakeIdMaker(const UINT32 nPlatId, const UINT32 nAreaId, const UINT32 nAppId) + { + Init(nPlatId, nAreaId, nAppId); + } + + void Init(const UINT32 nPlatId, const UINT32 nAreaId, const UINT32 nAppId) + { + m_nPlatformId = nPlatId; + m_nAreaId = nAreaId; + m_nAppId = nAppId; + } + + UINT64 GenId() + { + if (!IsEmpty()) + { + return PopFrontElement(); + } + + return NextId(); + } + + /** + * 获得下一个ID (该方法是线程安全的) + * @param bCanBlock 参数指定当前函数是否可以阻塞, 默认为true + * @return SnowflakeId + */ + UINT64 NextId(bool bCanBlock = true) + { + using namespace std; + +#ifdef SNOWFLAKE_ID_MAKER_NO_LOCK + static AtomicUInt64 nCurTimestamp{ 0 }; +#else + std::unique_lock oLock{ m_oMutex }; + AtomicUInt64 nCurTimestamp{ 0 }; +#endif + + nCurTimestamp = _GetNowMliTime(); + + // 如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常 + if (nCurTimestamp < m_nLastTimestamp) + { + std::ostringstream oSS; + oSS << "clock moved backwards. Refusing to generate id for " << m_nLastTimestamp - nCurTimestamp << " milliseconds"; + throw std::exception(std::runtime_error(oSS.str())); + } + + m_nGlobalSeq = (m_nGlobalSeq + 1) & SEQUENCE_MASK; + + // 为了使id递增+1均匀分布,这里seq跨毫秒也不清0 + if (m_nLastTimestamp == nCurTimestamp) + { + //遍历了一圈64个,等下一个毫秒生成 + if (m_nGlobalSeq == m_nMliSeq) + { + if (bCanBlock) + { + // 毫秒内序列溢出, 阻塞到下一个毫秒,获得新的时间戳 + nCurTimestamp = WaitForNextMilli(m_nLastTimestamp); + } + else + { + return 0; + } + } + } + else + { + m_nMliSeq = m_nGlobalSeq; + } + +#ifdef SNOWFLAKE_ID_MAKER_NO_LOCK + m_nLastTimestamp = nCurTimestamp.load(); +#else + m_nLastTimestamp = nCurTimestamp; +#endif + + // 移位并通过或运算拼到一起组成64位的ID + return ((nCurTimestamp - START_EPOCH) << TIME_STAMP_SHIFT) + | (m_nPlatformId << PLATFORM_ID_SHIFT) + | (m_nAreaId << AREA_ID_SHIFT) + | (m_nAppId << APP_ID_SHIFT) + | (m_nGlobalSeq); + } + + EUuidPoolState GetUuidDequeState() + { + if (m_deUuidPool.empty()) + { + return EUuidPoolState_Empty; + } + + size_t nCurPoolSize = m_deUuidPool.size(); + if (nCurPoolSize >= UUID_POOL_MAX_SIZE) + { + return EUuidPoolState_Full; + } + + uint32 nCurPercent = nCurPoolSize * 100 / UUID_POOL_MAX_SIZE; + if (nCurPercent < 30) + { + return EUuidPoolState_Not_Full_0_30_Per; + } + else if (nCurPercent < 70) + { + return EUuidPoolState_Not_Full_30_70_Per; + } + else + { + return EUuidPoolState_Not_Full_70_100_Per; + } + + return EUuidPoolState_Full; + } + + void FillPoolWithInMli() + { + EUuidPoolState nCurState = GetUuidDequeState(); + AtomicUInt64 nCurTimestamp = _GetNowMliTime(); + while (nCurState != EUuidPoolState_Full) + { + UINT64 nNextUUId = NextId(false); + if (nNextUUId == 0) + { + break; + } + + PushBackElement(nNextUUId); + + if (nCurTimestamp != _GetNowMliTime()) + { + break; + } + } + } + +protected: + + //判断pool是否为空 + bool IsEmpty() + { + return m_deUuidPool.empty(); + } + + //返回pool队列中front 元素 + UINT64 PopFrontElement() + { +#ifdef SNOWFLAKE_ID_MAKER_NO_LOCK + +#else + std::unique_lock oLock{ m_oMutex }; +#endif + + UINT64 nFrontElement = m_deUuidPool.front(); + m_deUuidPool.pop_front(); + return nFrontElement; + } + + void PushBackElement(UINT64 nNextUUId) + { +#ifdef SNOWFLAKE_ID_MAKER_NO_LOCK +#else + std::unique_lock oLock{ m_oMutex }; +#endif + m_deUuidPool.push_back(nNextUUId); + } + + /** + * 返回以毫秒为单位的当前时间 + * + * @return 当前时间(毫秒) + */ + UINT64 _GetNowMliTime() const + { + if (0) + { + Storm::CSTDateTime oDateTime; + oDateTime.Now(); + return oDateTime.EpochMilliSecs(); + } + else + { + using namespace std; + auto nTimeNow = chrono::system_clock::now(); + auto nDurationInMs = chrono::duration_cast(nTimeNow.time_since_epoch()); + return nDurationInMs.count(); + } + } + + /** + * 阻塞到下一个毫秒,直到获得新的时间戳 + * + * @param lastTimestamp 上次生成ID的时间截 + * @return 当前时间戳 + */ + UINT64 WaitForNextMilli(UINT64 lastTimestamp) const + { + UINT64 nTimestamp = _GetNowMliTime(); + while (nTimestamp <= lastTimestamp) + { + nTimestamp = _GetNowMliTime(); + } + return nTimestamp; + } + +private: + +#ifndef SNOWFLAKE_ID_MAKER_NO_LOCK + std::mutex m_oMutex; +#endif + + UINT32 m_nPlatformId = 0; //平台id + UINT32 m_nAreaId = 0; //区服id + UINT32 m_nAppId = 0; //Appid + AtomicUInt m_nGlobalSeq{ 0 }; //全局序列 + AtomicUInt m_nMliSeq{ 0 }; //每毫秒序列 + AtomicUInt64 m_nLastTimestamp{ 0 }; //上次生成ID的时间截 + std::deque m_deUuidPool; //uuid池 用于存放预生成uuids +}; + + + +/************************************************************************/ +/* 负责生成全局唯一id */ +/************************************************************************/ +class CUUIDMaker : public Storm::TSingleton +{ + friend class Storm::TSingleton; +public: + bool Init(const UINT32 nParaA, const UINT32 nParaB, const UINT32 nParaC) + { + m_oIdMaker.Init(nParaA, nParaB, nParaC); + return true; + } + + /// init with pipeid + bool Init(const UINT64 nPipeId) + { + using namespace Storm; + CServerID oServID(nPipeId); + m_oIdMaker.Init(oServID.GetPlat(), oServID.GetArea(), oServID.GetIndex()); + return true; + } + + UINT64 GenId() + { + return m_oIdMaker.GenId(); + } + + EUuidPoolState GetUuidDequeState() + { + return m_oIdMaker.GetUuidDequeState(); + } + + void FillPoolWithInMli() + { + m_oIdMaker.FillPoolWithInMli(); + } + + UINT64 GetCompareIdFromTime(UINT32 nTimeVal); + + UINT64 GetTimeAddVal(UINT32 nTimeVal); + + UINT32 GetTimeValFromUuid(UINT64 nUuid); + +private: + CSnowflakeIdMaker m_oIdMaker; +}; + +#define GEN_GLOBAL_UUID() CUUIDMaker::Instance()->GenId() + + +#endif + + +//gameserver +#include "uuid_pool_mgr.h" +#include "uuid_generator.h" + + +bool CUUIDPool::Init() +{ + FillUuidPool(); + return true; +} + +void CUUIDPool::OnTimer(unsigned int nId) +{ + EXLOG_DEBUG << "[RyzUuid]CUUIDPool::OnTimer Old EUuidPoolState : " << nId; + CUUIDMaker::Instance()->FillPoolWithInMli(); + FillUuidPool(); +} + +void CUUIDPool::FillUuidPool() +{ + EUuidPoolState eUuidDequeState = CUUIDMaker::Instance()->GetUuidDequeState(); + uint32 nUpdateInterval = 5 * 60 * 1000; + switch (eUuidDequeState) + { + case EUuidPoolState_Empty: + nUpdateInterval = 1 * 60 * 1000; + break; + case EUuidPoolState_Not_Full_0_30_Per: + nUpdateInterval = 2 * 60 * 1000; + break; + case EUuidPoolState_Not_Full_30_70_Per: + nUpdateInterval = 3 * 60 * 1000; + break; + case EUuidPoolState_Not_Full_70_100_Per: + nUpdateInterval = 4 * 60 * 1000; + break; + case EUuidPoolState_Full: + nUpdateInterval = 5 * 60 * 1000; + break; + default: + nUpdateInterval = 5 * 60 * 1000; + break; + } + + EXLOG_DEBUG << "[RyzUuid]CUUIDPool::OnTimer Now EUuidPoolState : " << eUuidDequeState; + SetTimer(eUuidDequeState, nUpdateInterval, nUpdateInterval, ETIMER_ONCE); +} + +``` +## 参考文章 +[分布式唯一id:snowflake算法思考 - 掘金](https://juejin.im/post/5a7f9176f265da4e721c73a8) +[https://tech.meituan.com/2017/04/21/mt-leaf.html](https://tech.meituan.com/MT_Leaf.html) +[GitHub - baidu/uid-generator: UniqueID generator](https://github.com/baidu/uid-generator) \ No newline at end of file diff --git "a/source/_posts/\345\215\225\347\224\237\344\272\247\350\200\205\345\215\225\346\266\210\350\264\271\350\200\205\347\216\257\345\275\242\347\274\223\345\206\262.md" "b/source/_posts/\345\215\225\347\224\237\344\272\247\350\200\205\345\215\225\346\266\210\350\264\271\350\200\205\347\216\257\345\275\242\347\274\223\345\206\262.md" new file mode 100644 index 0000000..00c48c5 --- /dev/null +++ "b/source/_posts/\345\215\225\347\224\237\344\272\247\350\200\205\345\215\225\346\266\210\350\264\271\350\200\205\347\216\257\345\275\242\347\274\223\345\206\262.md" @@ -0,0 +1,460 @@ + +--- +title: 单生产者单消费者环形缓冲 +date: 2020-01-23 15:12:50 +tags: ringbuff +categories: C/C++ +--- + +头文件 +```c++ +#ifndef _SIMPLE_LOOP_BUFFER_H__ +#define _SIMPLE_LOOP_BUFFER_H__ + +#include "gntype.h" +#include "gnmutex.h" +#include "gnlock.h" +#include "server_define.h" +using namespace Storm; + +typedef struct _tagBlock +{ + _tagBlock() + { + memset(pBuffer, 0, sizeof(char)*(MAX_CACHE_PACKET_SIZE + 1)); + } + ~_tagBlock() {}; + + char pBuffer[MAX_CACHE_PACKET_SIZE + 1]; + char* GetBuff() { return pBuffer; } + int32 GetBuffLen() { return MAX_CACHE_PACKET_SIZE + 1; } +}CBlock; + +class CSimpleLoopBufferEx +{ +public: + CSimpleLoopBufferEx(); + ~CSimpleLoopBufferEx(); + + void Clear(); + + /** + * @brief + * 初始化循环缓冲区 + * @param nSize : 初始化虚幻缓冲区的大小,实际大小为nSize+1 + * @return 成功返回true,失败返回false + */ + bool Init(); + + /** + * @brief + * 将需要存储的Buffer拷贝到循环缓冲区的结尾 + * @param pData : [输入参数]指向需要插入循环缓冲区的Buffer起始位置 + * @param nLen : 指向需要插入的Buffer的长度 + * @return 如果循环缓冲区拥有的大小大于等于nLen,返回true,否则返回false + * @remark 此函数不是线程安全的 + */ + INT32 PushBack(const CHAR *pData, INT32 nLen); + + /** + * @brief + * 从循环缓冲区的起始位置取nLen长度的Buffer,拷贝放入Buffer中 + * @param pBuf : [输入输出参数]获取数据的Buffer的起始指针 + * @param nLen : 需要读出的Buffer长度 + * @return 如果有足够所需读出的数据,返回true,否则返回false + * @remark 此函数不是线程安全的 + */ + INT32 PopFront(CHAR * &pBuf, INT32 nLen,CHAR* szData); + + /** + * @brief + * 丢弃nLen长度的数据 + * @param nLen : 需要丢弃的长度 + * @return void + * @remark 此函数不是线程安全的 + */ + bool DiscardFront(INT32 nLen); + + /** + * @brief + * 获取剩余可用空间大小 + * @return INT32 + * @remark 此函数不是线程安全的 + */ + INT32 GetFreeSpare(); + + /** + * @brief + * 拷贝内存中的数据 + * @param nReadOffSet : m_pNextRead的偏移量 + * @param nLen : 需要读出的Buffer长度 + * @remark 此函数不是线程安全的 + */ + INT32 GetData(INT32 nReadOffSet,INT32 nLen, CHAR* szData); + + +private: + CHAR *m_pBuffer; + CHAR *m_pNextRead; + CHAR *m_pNextWrite; + CHAR *m_pEnd; +}; + +#endif +``` + +实现 + +```c++ +#include "simpleloopbuffer.h" +#include +#include +#include "gndebug.h" +#include "../../common/commonloggerex.h" +#include "gate_factory.h" + + +using namespace Storm; + +CSimpleLoopBufferEx::CSimpleLoopBufferEx() : + m_pBuffer(NULL), + m_pNextRead(NULL), + m_pNextWrite(NULL), + m_pEnd(NULL) +{ +} + +CSimpleLoopBufferEx::~CSimpleLoopBufferEx() +{ + if (NULL != m_pBuffer) + { + CGateFactory::Instance()->ReleaseBlock((CBlock*)m_pBuffer); + m_pBuffer = NULL; + //delete[] m_pBuffer; + //m_pBuffer = NULL; + } +} + +void CSimpleLoopBufferEx::Clear() +{ + m_pNextRead = m_pBuffer; + m_pNextWrite = m_pBuffer; +} + +bool CSimpleLoopBufferEx::Init() +{ + //m_pBuffer = SDNew char[nSize + 1]; + CBlock* pBlock = CGateFactory::Instance()->CreateBlock(); + if (!pBlock) + return false; + + m_pBuffer = pBlock->GetBuff(); + if (NULL == m_pBuffer) + { + return false; + } + m_pNextRead = m_pBuffer; + m_pNextWrite = m_pBuffer; + //m_pEnd = m_pBuffer + nSize + 1; + m_pEnd = m_pBuffer + pBlock->GetBuffLen(); + return true; +} + +INT32 CSimpleLoopBufferEx::PushBack(const CHAR *pData, INT32 nLen) +{ + CHAR* poRead = m_pNextRead; + if (m_pNextWrite >= poRead) + { + // 1、尾部指针减去写起始位置小于 nLen + // 2、读的起始位置减去内存首地址 小于nLen + // 内存空间不够,不能将数据写入缓存 + /* == 内存模型 == + (empty) m_pNextRead m_pNextWrite (empty) + |----------------------------------|--------------------|---------------------| + */ + INT32 nRight = m_pEnd - m_pNextWrite; + INT32 nLeft = poRead - m_pBuffer; + if (nLeft + nRight <= nLen) + { + return -1; + } + } + else + { + /* == 内存模型 == + m_pNextWrite (empty) m_pNextRead + |----------------------------------|--------------------|---------------------| + */ + if (poRead - m_pNextWrite <= nLen) + { + return -2; + } + } + // else // m_pNextWrite == m_pNextRead + // { + // if (m_pNextWrite > m_pBuffer) + // { + // m_oMutex.Unlock(); + // return -3; + // } + // } + if (m_pEnd - m_pNextWrite > nLen) + { + memcpy(m_pNextWrite, pData, nLen); + m_pNextWrite += nLen; + } + else if (m_pEnd - m_pNextWrite == nLen) + { + memcpy(m_pNextWrite, pData, nLen); + m_pNextWrite = m_pBuffer; + } + else + { + INT32 nStartLen = m_pEnd - m_pNextWrite; + memcpy(m_pNextWrite, pData, nStartLen); + memcpy(m_pBuffer, pData + nStartLen, nLen - nStartLen); + m_pNextWrite = m_pBuffer + nLen - nStartLen; + } + return nLen; +} + +INT32 CSimpleLoopBufferEx::PopFront(CHAR * &pBuf, INT32 nLen, CHAR* szData) +{ + CHAR* poNextWrite = m_pNextWrite; + if (poNextWrite == m_pNextRead) + { + EXLOG_ERROR << "[%s:%d]:PopFront failed! ErrCode:-1"; + return -1; + } + if (poNextWrite > m_pNextRead) + { + /* == 内存模型 == + (empty) m_pNextRead m_pNextWrite (empty) + |----------------------------------|--------------------|---------------------| + */ + if (poNextWrite - m_pNextRead < nLen) + { + EXLOG_ERROR << "PopFront failed! ErrCode:-2"; + return -2; + } + } + else + { + /* == 内存模型 == + m_pNextWrite (empty) m_pNextRead + |----------------------------------|--------------------|---------------------| + */ + INT32 nRight = m_pEnd - m_pNextRead; + INT32 nLeft = poNextWrite - m_pBuffer; + if (nLeft + nRight < nLen) + { + EXLOG_ERROR << "[%s:%d]:PopFront failed! ErrCode:-3"; + return -3; + } + } + if (m_pEnd - m_pNextRead > nLen) + { + memcpy(szData, m_pNextRead, nLen); + m_pNextRead += nLen; + } + else if (m_pEnd - m_pNextRead == nLen) + { + memcpy(szData, m_pNextRead, nLen); + m_pNextRead = m_pBuffer; + } + else + { + INT32 nStartLen = m_pEnd - m_pNextRead; + memcpy(szData, m_pNextRead, nStartLen); + memcpy(szData + nStartLen, m_pBuffer, nLen - nStartLen); + m_pNextRead = m_pBuffer + nLen - nStartLen; + } + return nLen; +} + + +bool CSimpleLoopBufferEx::DiscardFront(INT32 nLen) +{ + CHAR* poNextWrite = m_pNextWrite; + if (poNextWrite == m_pNextRead) + { + EXLOG_ERROR << "[%s:%d]:PopFront failed! ErrCode:-1"; + return false; + } + if (poNextWrite > m_pNextRead) + { + /* == 内存模型 == + (empty) m_pNextRead m_pNextWrite (empty) + |----------------------------------|--------------------|---------------------| + */ + if (poNextWrite - m_pNextRead < nLen) + { + EXLOG_ERROR << "[%s:%d]:PopFront failed! ErrCode:-2"; + return false; + } + } + else + { + /* == 内存模型 == + m_pNextWrite (empty) m_pNextRead + |----------------------------------|--------------------|---------------------| + */ + INT32 nRight = m_pEnd - m_pNextRead; + INT32 nLeft = poNextWrite - m_pBuffer; + if (nLeft + nRight < nLen) + { + EXLOG_ERROR << "[%s:%d]:PopFront failed! ErrCode:-3"; + return false; + } + } + if (m_pEnd - m_pNextRead > nLen) + { + m_pNextRead += nLen; + } + else if (m_pEnd - m_pNextRead == nLen) + { + m_pNextRead = m_pBuffer; + } + else + { + INT32 nStartLen = m_pEnd - m_pNextRead; + m_pNextRead = m_pBuffer + nLen - nStartLen; + } + + return true; +} + +INT32 CSimpleLoopBufferEx::GetFreeSpare() +{ + CHAR* poRead = m_pNextRead; + if (m_pNextWrite >= poRead) + { + // 1、尾部指针减去写起始位置小于 nLen + // 2、读的起始位置减去内存首地址 小于nLen + // 内存空间不够,不能将数据写入缓存 + /* == 内存模型 == + (empty) m_pNextRead m_pNextWrite (empty) + |----------------------------------|--------------------|---------------------| + */ + INT32 nRight = m_pEnd - m_pNextWrite; + INT32 nLeft = poRead - m_pBuffer; + return nLeft + nRight; + + } + else + { + /* == 内存模型 == + m_pNextWrite (empty) m_pNextRead + |----------------------------------|--------------------|---------------------| + */ + + return poRead - m_pNextWrite; + } +} + +INT32 CSimpleLoopBufferEx::GetData(INT32 nReadOffSet, INT32 nLen, CHAR* szData) +{ + //指针偏移 + CHAR* poNextWrite = m_pNextWrite; + if (poNextWrite == m_pNextRead) + { + EXLOG_ERROR << "[%s:%d]:PopFront failed! ErrCode:-1"; + return -1; + } + + CHAR *pReadOffSet = m_pNextRead + nReadOffSet; + if (poNextWrite > m_pNextRead) + { + /* == 内存模型 == + (empty) m_pNextRead m_pNextWrite (empty) + |----------------------------------|--------------------|---------------------| + */ + + if (pReadOffSet >= m_pNextWrite) + { + EXLOG_ERROR << "GetData failed! ErrCode:-2"; + return -2; + } + } + else + { + /* == 内存模型 == + m_pNextWrite (empty) m_pNextRead + |----------------------------------|--------------------|---------------------| + */ + + if (pReadOffSet < m_pEnd) + { + + } + else if (pReadOffSet == m_pEnd) + { + pReadOffSet = m_pBuffer; + if (pReadOffSet >= m_pNextWrite) + { + EXLOG_ERROR << "GetData failed! ErrCode:-3"; + return -3; + } + } + else { + pReadOffSet = m_pBuffer + nReadOffSet - (m_pEnd - m_pNextRead); + if (pReadOffSet >= m_pNextWrite) + { + EXLOG_ERROR << "GetData failed! ErrCode:-4"; + return -4; + } + } + } + + //拷贝数据 + + if (poNextWrite > pReadOffSet) + { + /* == 内存模型 == + (empty) m_pNextRead m_pNextWrite (empty) + |----------------------------------|--------------------|---------------------| + */ + if (poNextWrite - pReadOffSet < nLen) + { + EXLOG_ERROR << "PopFront failed! ErrCode:-5"; + return -5; + } + } + else + { + /* == 内存模型 == + m_pNextWrite (empty) m_pNextRead + |----------------------------------|--------------------|---------------------| + */ + INT32 nRight = m_pEnd - pReadOffSet; + INT32 nLeft = poNextWrite - m_pBuffer; + if (nLeft + nRight < nLen) + { + EXLOG_ERROR << "[%s:%d]:PopFront failed! ErrCode:-6"; + return -6; + } + } + if (m_pEnd - pReadOffSet > nLen) + { + memcpy(szData, pReadOffSet, nLen); + //pReadOffSet += nLen; + } + else if (m_pEnd - pReadOffSet == nLen) + { + memcpy(szData, pReadOffSet, nLen); + //pReadOffSet = m_pBuffer; + } + else + { + INT32 nStartLen = m_pEnd - pReadOffSet; + memcpy(szData, pReadOffSet, nStartLen); + memcpy(szData + nStartLen, m_pBuffer, nLen - nStartLen); + //m_pNextRead = m_pBuffer + nLen - nStartLen; + } + + + return nLen; + +} + +``` \ No newline at end of file diff --git "a/source/_posts/\345\220\204\347\261\273APP\346\216\222\350\241\214\346\246\234\345\256\236\347\216\260.md" "b/source/_posts/\345\220\204\347\261\273APP\346\216\222\350\241\214\346\246\234\345\256\236\347\216\260.md" new file mode 100644 index 0000000..4b5b13d --- /dev/null +++ "b/source/_posts/\345\220\204\347\261\273APP\346\216\222\350\241\214\346\246\234\345\256\236\347\216\260.md" @@ -0,0 +1,167 @@ +--- +title: 各类APP排行榜实现 +date: 2020-01-23 15:12:45 +tags: rank +categories: Game +--- + +## 需求背景 +1. 查看TopN的用户排名 +2. 查看自己的排名 +3. 用户积分变更后,排名及时更新 + +## 实现方案 +### 方案一 利用MYSQL 排序 +利用MySQL来实现,存放一张用户积分表user_score +取前top N,自己的排名都可以通过简单的sql语句搞定。 +算法简单,利用sql的功能,不需要其他复杂逻辑,对于数据量比较少、性能要求不高,可以使用。但是对于海量数据,性能是无法接受的。可能会导致全局锁表之类的问题。 + +### 方案二 内存数组排序 +在内存中预分配所要排名用户大小的数组,所有的积分排名变更基于此移动元素,成熟排序算法有最小/大堆、快速排序等,这种方案优点是当数据量小的时候,简单快捷,容易实现,不需要其他任何组件支持,但是当面对海量数据的时候数组空间占用可能不太现实 + +### 方案三 利用GCC库支持 +具体的,用GCC的pb_ds库中有assoc_container来进行实现。 +参考[tree_order_statistics.cc](https://opensource.apple.com/source/llvmgcc42/llvmgcc42-2336.9/libstdc++-v3/testsuite/ext/pb_ds/example/tree_order_statistics.cc): +```cpp +#include +#include +#include + +using namespace std; +using namespace pb_ds; +using namespace pb_ds; + +// A red-black tree table storing ints and their order +// statistics. Note that since the tree uses +// tree_order_statistics_node_update as its update policy, then it +// includes its methods by_order and order_of_key. +typedef +tree< + int, + null_mapped_type, + less, + rb_tree_tag, + // This policy updates nodes' metadata for order statistics. + tree_order_statistics_node_update> +set_t; + +int main() +{ + // Insert some entries into s. + set_t s; + s.insert(12); + s.insert(505); + s.insert(30); + s.insert(1000); + s.insert(10000); + s.insert(100); + + // The order of the keys should be: 12, 30, 100, 505, 1000, 10000. + assert(*s.find_by_order(0) == 12); + assert(*s.find_by_order(1) == 30); + assert(*s.find_by_order(2) == 100); + assert(*s.find_by_order(3) == 505); + assert(*s.find_by_order(4) == 1000); + assert(*s.find_by_order(5) == 10000); + assert(s.find_by_order(6) == s.end()); + + // The order of the keys should be: 12, 30, 100, 505, 1000, 10000. + assert(s.order_of_key(10) == 0); + assert(s.order_of_key(12) == 0); + assert(s.order_of_key(15) == 1); + assert(s.order_of_key(30) == 1); + assert(s.order_of_key(99) == 2); + assert(s.order_of_key(100) == 2); + assert(s.order_of_key(505) == 3); + assert(s.order_of_key(1000) == 4); + assert(s.order_of_key(10000) == 5); + assert(s.order_of_key(9999999) == 6); + + // Erase an entry. + s.erase(30); + + // The order of the keys should be: 12, 100, 505, 1000, 10000. + assert(*s.find_by_order(0) == 12); + assert(*s.find_by_order(1) == 100); + assert(*s.find_by_order(2) == 505); + assert(*s.find_by_order(3) == 1000); + assert(*s.find_by_order(4) == 10000); + assert(s.find_by_order(5) == s.end()); + + // The order of the keys should be: 12, 100, 505, 1000, 10000. + assert(s.order_of_key(10) == 0); + assert(s.order_of_key(12) == 0); + assert(s.order_of_key(100) == 1); + assert(s.order_of_key(505) == 2); + assert(s.order_of_key(707) == 3); + assert(s.order_of_key(1000) == 3); + assert(s.order_of_key(1001) == 4); + assert(s.order_of_key(10000) == 4); + assert(s.order_of_key(100000) == 5); + assert(s.order_of_key(9999999) == 5); + + return 0; +} +``` +存取效率都可以达到O(log(n)),不足就是程序重启后数据会丢失。还是对所有的用户积分,没必要。 +而且是有依赖,不方便扩展,实现不了复杂的需求, + +### 方案四 实现排序树 + +大致实现思路如下: + + +  我们可以把[0, 1,000,000)作为一级区间;再把一级区间分为两个2级区间[0, 500,000), [500,000, 1,000,000),然后把二级区间二分为4个3级区间[0, 250,000), [250,000, 500,000), [500,000, 750,000), [750,000, 1,000,000),依此类推,最终我们会得到1,000,000个21级区间[0,1), [1,2) … [999,999, 1,000,000)。这实际上是把区间组织成了一种平衡二叉树结构,根结点代表一级区间,每个非叶子结点有两个子结点,左子结点代表低分区间,右子结点代表高分区间。树形分区结构需要在更新时保持一种不变量,非叶子结点的count值总是等于其左右子结点的count值之和。 + + +  以后,每次用户积分有变化所需要更新的区间数量和积分变化量有关系,积分变化越小更新的区间层次越低。总体上,每次所需要更新的区间数量是用户积分变量的log(n)级别的,也就是说如果用户积分一次变化在百万级,更新区间的数量在二十这个级别。在这种树形分区积分表的辅助下查询积分为s的用户排名,实际上是一个在区间树上由上至下、由粗到细一步步明确s所在位置的过程。比如,对于积分499,000,我们用一个初值为0的排名变量来做累加;首先,它属于1级区间的左子树[0, 500,000),那么该用户排名应该在右子树[500,000, 1,000,000)的用户数count之后,我们把该count值累加到该用户排名变量,进入下一级区间;其次,它属于3级区间的[250,000, 500,000),这是2级区间的右子树,所以不用累加count到排名变量,直接进入下一级区间;再次,它属于4级区间的…;直到最后我们把用户积分精确定位在21级区间[499,000, 499,001),整个累加过程完成,得出排名! + + +  虽然,本算法的更新和查询都涉及到若干个操作,但如果我们为区间的from_score和to_score建立索引,这些操作都是基于键的查询和更新,不会产生表扫描,因此效率更高。另外,本算法并不依赖于关系数据模型和SQL运算,可以轻易地改造为NoSQL等其他存储方式,而基于键的操作也很容易引入缓存机制进一步优化性能。进一步,我们可以估算一下树形区间的数目大约为2,000,000,考虑每个结点的大小,整个结构只占用几十M空间。所以,我们完全可以在内存建立区间树结构,并通过user_score表在O(n)的时间内初始化区间树,然后排名的查询和更新操作都可以在内存进行。一般来讲,同样的算法,从数据库到内存算法的性能提升常常可以达到10^5以上;因此,本算法可以达到非常高的性能。 + + +  算法特点 + + +  优点:结构稳定,不受积分分布影响;每次查询或更新的复杂度为积分最大值的O(log(n))级别,且与用户规模无关,可以应对海量规模;不依赖于SQL,容易改造为NoSQL或内存数据结构。 + + +  缺点:算法相对更复杂。 + +### 方案五 实现跳表排序 + +skip list是链表的一种特殊形式,对链表的一种优化;保证INSERT和REMOVE操作是O(logn),而通用链表的复杂度为O(n); +优点:实现较简单,效率基本上O(log(N)) +缺点:当达到亿级别时的数据时,性能会急剧下降 + +### 方案六 利用redis特新实现 +其实redis底层还是使用跳表实现排序的,只是将接口都封装好了,使用接口也比较完善,稳定。 + +redis的zset天生是用来做排行榜的、好友列表, 去重, 历史记录等业务需求。接口使用非常简单。接口非常丰富,基本上需要的实现都能满足,说明如下: + +ZAdd/ZRem是O(log(N)),ZRangeByScore/ZRemRangeByScore是O(log(N)+M),N是Set大小,M是结果/操作元素的个数。 + +ZSET的实现用到了两个数据结构:hash table 和 skip list(跳跃表),其中hash table是具体使用redis中的dict来实现的,主要是为了保证查询效率为O(1) ,而skip list(跳跃表)主要是保证元素有序并能够保证INSERT和REMOVE操作是O(logn)的复杂度。 + +优点:基于redis开发,速度快;使用redis相关特性 + +缺点:当达到亿级别时的数据时,性能会急剧下降 + +来实现排行榜的方法很多,可以根据自己的具体需求,参考选用。 + +## 方案七 其实只需要TopN的排名,大于N的排名并不需要精确排名计算 + +基于此,假设我们游戏内只需要排前100名,这里我们只需要维护一个100大小的数组 +1. 当元素A需要参与排序的时候,与数组中最小的积分进行比较,如果能进100名,那么将第100剔除,将A加入,并记录最小元素,这样就完成了积分上涨的情况 +2. 还有一种就是已经在前100名中元素的积分发生变化下降,那么需要在前100名后找出可以进排行榜的元素,这种情况比较麻烦,可以使用最大堆保存剩下所有用户的数据,当需要找出能替换进入排行榜的元素就非常快logn,选择特定数据结构非常重要 +3. 既然大于N的用户不需要精确排名,那么怎么样估算大概排名呢?一般做法是按照数值区间建立所若干个桶,比如我们预计要排名的那一个数据的最大值能到1W。我建立0-10, 10-100,100-1000, 1000-2000, 2000-5000, 5000-10000 这样6个桶,每个桶里面记录分值在这个桶对应的区间内,有多少个玩家。 比如 +0-10, 10人 +10-100,20人 +100-1000,30人 +1000-2000, 40人 +2000-5000, 50人 +5000-10000, 60人 +那么如果一个玩家 是 1234分,那么他的排名就超过了 (10 + 20 + 30)/ (10 + 20 + 30 + 40 + 50 + 60)这个百分比的玩家(所以桶分的越细,后面的排名越精确) +实质就是按照分区间记录区间内元素个数,从而估算大概排名,因此数值区间越小,估算约精确。 + + diff --git "a/source/_posts/\346\211\271\351\207\217\346\215\242\350\241\214\347\254\246\350\275\254\346\215\242CRLF\345\210\260LF.md" "b/source/_posts/\346\211\271\351\207\217\346\215\242\350\241\214\347\254\246\350\275\254\346\215\242CRLF\345\210\260LF.md" new file mode 100644 index 0000000..09f8d03 --- /dev/null +++ "b/source/_posts/\346\211\271\351\207\217\346\215\242\350\241\214\347\254\246\350\275\254\346\215\242CRLF\345\210\260LF.md" @@ -0,0 +1,53 @@ +--- +title: 批量转换换行符CRLF到LF +date: 2020-02-12 13:25:41 +tags: git +--- + +## 关闭Git自动转换功能 + +``` +git config --global core.autocrlf false +``` + +## CRLF转换成LF + +**vscode或者visual studio等一些代码编辑器仅仅支持单个文件的格式转换,以下三种方法均是支持批量转换的方法** + +### 第一种方法 (亲测)) + +已经上传到`/y-server/doc/format/` 目录下 或者点击下面链接下载最新 + +[下载dos2unix工具包](https://waterlan.home.xs4all.nl/dos2unix/dos2unix-7.4.1-win64.zip) + +[dos2unix文档说明](https://waterlan.home.xs4all.nl/dos2unix/zh_CN/man1/dos2unix.htm#9) + +结合 find(1) 和 xargs(1) 使用 dos2unix 可以递归地转换目录树中的文本文件。例如,转换当前目录的目录树中所有的 .txt 文件: +``` +dos2unix < a.txt +cat a.txt | dos2unix +``` +若文件名中有空格或引号,则需要使用 find(1) 选项 -print0 及相应的 xargs(1) 选项 -0;其他情况下则可以省略它们。也可以结合 -exec 选项来使用 find(1): +``` +find . -name '*.txt' -exec dos2unix {} \; +``` +在Windows命令提示符中,可以使用下列命令: +``` +for /R %G in (*.txt) do dos2unix "%G" +``` +PowerShell用户可以在Windows PowerShell中使用如下命令: +``` +get-childitem -path . -filter '*.txt' -recurse | foreach-object {dos2unix $_.Fullname} +``` + +### 第二种方法 (没有尝试) + +采用EditPlus批量转换文件格式 + +![1](https://img-blog.csdnimg.cn/20190414011144786.png) +![2](https://img-blog.csdnimg.cn/20190414011207506.png) +![2](https://img-blog.csdnimg.cn/20190414011249745.png) + +### 第三种方法 (没有尝试) + +[巧妙的借助git快速批量转换crlf到lf](https://rizon.top/tech/%E8%BD%AC%E6%8D%A2crlf%E5%88%B0lf/) \ No newline at end of file diff --git "a/source/_posts/\346\267\261\345\205\245\346\265\205\345\207\272\346\227\266\351\227\264\350\275\256Timer\345\256\232\346\227\266\345\231\250.md" "b/source/_posts/\346\267\261\345\205\245\346\265\205\345\207\272\346\227\266\351\227\264\350\275\256Timer\345\256\232\346\227\266\345\231\250.md" new file mode 100644 index 0000000..a46642a --- /dev/null +++ "b/source/_posts/\346\267\261\345\205\245\346\265\205\345\207\272\346\227\266\351\227\264\350\275\256Timer\345\256\232\346\227\266\345\231\250.md" @@ -0,0 +1,495 @@ +--- +title: Timer 定时器技术分享 +date: 2020-01-23 15:12:45 +tags: timer +categories: Linux +--- + +## 说点废话 +> 不管是客户端`Client`还是服务器`Server`,不论你是从事游戏行业还是互联网行业,在技术上总会涉及到定时器。虽然有的框架系统已经帮你实现,并且提供完美API供你使用,但你真的了解定时器吗?我们不仅要知道如何使用正确的Timer,还得明白定时器的实现原理,要知其所以然。 + +## 理解定时器 +使用者角度分类: +1. 周期性定时器 + 1. 使用 `TCP` 长连接时,客户端需要定时向服务端发送心跳请求 + 2. 游戏内系统每日重置功能 + 3. 体力回复 + 4. .... + +2. 非周期性定时器 + 1. 玩法活动定时开启、关闭 + 2. ... + +**当然,大部分非周期性定时器都可以使用周期性定时器实现,即执行一次后立即调用Remove接口即可** + +定时器像水和空气一般,普遍存在于各个场景中,一般定时任务的形式表现为:经过固定时间后触发、按照固定频率周期性触发、在某个时刻触发。定时器是什么?可以理解为这样一个数据结构:**存储一系列的任务集合,并且 `Deadline` 越接近的任务,拥有越高的执行优先级** + +支持以下几种操作: +1. `Add New TimerTask` 添加新的定时器 +2. `Kill Or Remove TimerTask` 取消或者移除既有定时器任务 +3. `Run` 执行 + +判断一个`TimerTask`是否到期,基本会采用轮询的方式,每隔一个**时间片**`tickDuration`去检查最近的任务是否到期。 + +> **说到底,定时器还是靠线程轮询实现的。** + +现在知道`Timer`是靠轮询来实现的,那么中间应该采用那种数据结构呢?采用不同的数据结构实现,其性能也大不一样! +现在主要有如下几种:`List`链表、`Heap`最小堆、时间轮、分级时间轮,其中时间轮的实质为Hash表。 + +## 数据结构 + +### 双向有序链表 +`AddTimer O(N) `很容易理解,按照 `expireTime` 查找合适的位置即可;`KillTimer O(1)` ,任务在 `Kill` 时,会持有自己节点的引用,所以不需要查找其在链表中所在的位置,即可实现当前节点的删除;`RunTimer O(1)`,由于整个双向链表是基于 expireTime 有序的,所以调度器只需要轮询第一个任务即可。 + +### 最小堆 +最小堆指的是满足除了根节点以外的每个节点都不小于其父节点的堆。这样,堆中的最小值就存放在根节点中,并且在以某个结点为根的子树中,各节点的值都不小于该子树根节点的值。一个最小堆的例子如下图: +![最小堆](https://www.ibm.com/developerworks/cn/linux/l-cn-timers/image002.jpg) + +明显的,最小堆添加新元素或者删除节点效率为`O(lgn)`, `root`节点`expireTime`最小,执行优先级最高,因此复杂度为O(1) + +**如果程序中的定时器数量比较少,基于最小堆的定时器一般可以满足需求,且实现简单。** + +### 时间轮 +时间轮的实质为哈希环`HashTable`,每个定时器任务根据对其`expireTime`哈希,得到对应的位置`index`,复杂度为`O(1)` +![时间轮](http://kirito.iocoder.cn/201807171109599678a80c-075a-40ee-b25f-10fd82c1025c.png) + +**性能比较:** +| 实现方式 | AddTimer | KillTimer | RunTimer| +|-------- |------------| -----------| -----------| +| 基于链表 | O(1) | O(n) | O(n)| +|基于排序链表 |O(n) |O(1) |O(1)| +|基于最小堆 | O(lgn)| O(lgn)| O(1)| +|基于时间轮 |O(1) |O(1) |O(1)| + + +*现在看起来我们选择时间轮来实现就行了,是否这样就完事了?* + +## 着重分析时间轮 + +如果需要支持的定时器范围非常的大,上面的实现方式则不能满足这样的需求。因为这样将消耗非常可观的内存,假设需要表示的定时器范围为:0 – 2^3-1ticks,则简单时间轮需要 2^32 个元素空间,这对于内存空间的使用将非常的庞大。也许可以降低定时器的精度,使得每个 Tick 表示的时间更长一些,但这样的代价是定时器的精度将大打折扣。 + +现在的问题是,度量定时器的粒度,只能使用唯一粒度吗?想想日常生活中常遇到的水表,如下图: +![水表](https://www.ibm.com/developerworks/cn/linux/l-cn-timers/image004.jpg) + +钟表: +![水表](https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1556440651&di=0c604969bfdf6b335dd78462f862743e&imgtype=jpg&er=1&src=http%3A%2F%2Famuseum.cdstm.cn%2FAMuseum%2Ftime%2F01gzsj%2Fimages%2F0102_b.jpg) + +分级时间轮同样如此,每级时间轮所代表的粒度精度都不一样,这样结合起来,既能够保证定时器的精度,也能以较小内存代价表示范围更大更多的定时器。 + +**简单时间轮**: 一个齿轮,每个齿轮保存一个超时的node链表。一个齿轮表示一个时间刻度,比如钟表里面一小格代表一秒,钟表的秒针每次跳一格。假设一个刻度代表10ms,则2^32 个格子可表示1.36年,2^16个格子可表示10.9分钟。当要表示的时间范围较大时,空间复杂度会大幅增加。 + +**分级时间轮**: 类似于水表,当小轮子里的指针转动满一圈后,上一级轮子的指针进一格。 采用五个轮子每个轮子为一个简单时间轮,大小分别为 2^8, 2^6, 2^6, 2^6, 2^6,所需空间:2^8 + 2^6 + 2^6 + 2^6 + 2^6 = 512, 可表示的范围为 0 -- 2^8 * 2^6 * 2^6* 2^6* 2^6 = 2^32 。 + +**分级时间轮简洁图**: +![分级时间轮](http://kirito.iocoder.cn/7f03c027b1de345a0b1e57239d73de74.png) + +*熟知的Linux系统内核,定时器实现方式就是分级时间轮* +![Linux内核](https://images0.cnblogs.com/i/205989/201405/281755599318276.jpg) + +## 具体实现 +wheel_timer_mgr.h +```cpp +class CWheelTimerModule; +class ITimerMgr; +class CWheelTimer; + +typedef std::list TListTimer; + +enum ETimerType +{ + ETIMER_ONCE, + ETIMER_CIRCLE +}; + +///定时器最小精度 1/10秒 (100毫秒) +static const int WHEEL_TIMER_MIN_PRECISION = 100; + +/** +* @brief 基于分级的时间轮定时器, 精度约定为十分之一秒 +* 注意,注册的Timer精度必须为约定的精度倍数 +*/ +class CWheelTimer +{ +public: + CWheelTimer(); + CWheelTimer(CWheelTimerModule& oModule); + ~CWheelTimer(); + + /** + * @brief 启动定时器 + * @param nInterval: 传入的是毫秒, 约定必须是十分之一秒(100ms)的倍数 + * @return + */ + void Start(ITimerMgr* pITimer, unsigned int nId, unsigned nInterval, int nDelay, ETimerType eTimerType = ETIMER_CIRCLE); + + /** + * @brief 停止定时器 + */ + void Stop(); +private: + /** + * @brief 定时器被触发 + */ + void OnTrigger(const UINT64 nNow); + +private: + friend class CWheelTimerModule; + + CWheelTimerModule& m_oModule; + ITimerMgr* m_pTimerMgr = nullptr; + ETimerType m_eTimerType; + unsigned int m_nTimerId = 0; + unsigned int m_nInterval = -1; //ms + unsigned long long m_llExpireTime = 0; //ms + int m_nVecIndex = 0; + TListTimer::iterator m_listIter; +}; + +/** +* @brief 定时器管理器接口, 派生类继承使用 +*/ +class ITimerMgr +{ +public: + virtual ~ITimerMgr(); + virtual void OnTimer(unsigned int nId) = 0; + //interval 时间精度 ms + void SetTimer(unsigned int nId, int nInterval, int nDelay = 0, ETimerType eTimeType = ETIMER_CIRCLE); + void KillTimer(unsigned int nId); + bool IsTimerExist(unsigned int nId);; + +protected: + std::unordered_map m_mapTimer; +}; + +/** +* @brief 全局定时器管理模块, 负责管理所有的定时器 +*/ +class CWheelTimerModule : public Storm::TSingleton +{ + friend class Storm::TSingleton; + CWheelTimerModule(); +public: + void AddTimer(CWheelTimer* pTimer); + void RemoveTimer(CWheelTimer* pTimer); + /** + * @brief 驱动所有的定时器 + */ + void Run(); + + static UINT64 GetCurMillisecs(); + /** + * @brief 修正精度 + * @param nSrcTime + * @return 传入的时间除以当前约定最小定时器精度 + */ + static UINT64 HandlePrecision(const UINT64 nSrcTime); + +private: + int _Cascade(int nOffset, int nIndex); + +private: + std::vector m_vecTimerList; + + //notice: precision=100ms not 1ms + UINT64 m_llCheckTime; +}; + +/** +* @brief 定时器工厂 +*/ +class CTimerFactory: public Storm::TSingleton, public CNoncopy +{ + friend class Storm::TSingleton; + CTimerFactory(); + virtual ~CTimerFactory(); +public: + CWheelTimer* CreateCTimer(); + void ReleaseCTimer(CWheelTimer* pTimer); +private: + + CSTObjectPool m_oCTimerPool; +} +``` + +wheel_timer_mgr.cpp +```cpp +#if 0 + #define TVN_BITS 6 + #define TVR_BITS 8 + #define TVN_SIZE (1 << TVN_BITS) + #define TVR_SIZE (1 << TVR_BITS) + #define TVN_MASK (TVN_SIZE - 1) + #define TVR_MASK (TVR_SIZE - 1) + #define OFFSET(N) (TVR_SIZE + (N) *TVN_SIZE) + #define INDEX(V, N) ((V >> (TVR_BITS + (N) *TVN_BITS)) & TVN_MASK) +#else + static const int TVN_BITS = 6; + static const int TVR_BITS = 8; + static const int TVN_SIZE = (1 << TVN_BITS); + static const int TVR_SIZE = (1 << TVR_BITS); + static const int TVN_MASK = (TVN_SIZE - 1); + static const int TVR_MASK = (TVR_SIZE - 1); + int OFFSET(int N) { return (TVR_SIZE + (N)*TVN_SIZE); } + int INDEX(unsigned long long V, int N) + { + return ((V >> (TVR_BITS + (N)*TVN_BITS)) & TVN_MASK); + } + +#endif + + + +CWheelTimer::CWheelTimer() + :m_oModule(CWheelTimerModule::GetInstance()) + , m_nVecIndex(-1) +{ +} + +CWheelTimer::CWheelTimer(CWheelTimerModule& oModule) + : m_oModule(oModule) + , m_nVecIndex(-1) +{ +} + +CWheelTimer::~CWheelTimer() +{ + Stop(); +} + +void CWheelTimer::Start(ITimerMgr* pTimerMgr, unsigned int nId, unsigned nInterval, int nDelay, ETimerType eTimerType) +{ + Stop(); + + //时间都修正为最小精度 + if (nInterval < WHEEL_TIMER_MIN_PRECISION) + { + nInterval = WHEEL_TIMER_MIN_PRECISION; + } + + m_nInterval = CWheelTimerModule::HandlePrecision(nInterval); + m_eTimerType = eTimerType; + m_pTimerMgr = pTimerMgr; + m_nTimerId = nId; + m_llExpireTime = CWheelTimerModule::HandlePrecision(nDelay + CWheelTimerModule::GetCurMillisecs()); + m_oModule.AddTimer(this); +} + + +void CWheelTimer::Stop() +{ + if (m_nVecIndex != -1) + { + m_oModule.RemoveTimer(this); + m_nVecIndex = -1; + } +} + +void CWheelTimer::OnTrigger(const UINT64 nNow) +{ + if (m_eTimerType == ETIMER_CIRCLE) + { + m_llExpireTime = m_nInterval + nNow; + m_oModule.AddTimer(this); + } + else + { + m_nVecIndex = -1; + } + + if (m_pTimerMgr != nullptr) + { + m_pTimerMgr->OnTimer(m_nTimerId); + } +} + +//-------------------------------------------------------------------------------------------------------- + +CWheelTimerModule::CWheelTimerModule() +{ + m_vecTimerList.resize(TVR_SIZE + 4 * TVN_SIZE); + m_llCheckTime = HandlePrecision(GetCurMillisecs()); +} + +void CWheelTimerModule::AddTimer(CWheelTimer* pTimer) +{ + UINT64 llExpireTime = pTimer->m_llExpireTime; + INT64 llTimeDiff = pTimer->m_llExpireTime - m_llCheckTime; + + if (llTimeDiff < 0) + { + pTimer->m_nVecIndex = m_llCheckTime & TVR_MASK; + } + else if (llTimeDiff < TVR_SIZE) + { + pTimer->m_nVecIndex = llExpireTime & TVR_MASK; + } + else if (llTimeDiff < 1 << (TVR_BITS + TVN_BITS)) + { + pTimer->m_nVecIndex = OFFSET(0) + INDEX(llExpireTime, 0); + } + else if (llTimeDiff < 1 << (TVR_BITS + 2 * TVN_BITS)) + { + pTimer->m_nVecIndex = OFFSET(1) + INDEX(llExpireTime, 1); + } + else if (llTimeDiff < 1 << (TVR_BITS + 3 * TVN_BITS)) + { + pTimer->m_nVecIndex = OFFSET(2) + INDEX(llExpireTime, 2); + } + else + { + if (llTimeDiff > 0xffffffffUL) + { + llTimeDiff = 0xffffffffUL; + llExpireTime = llTimeDiff + m_llCheckTime; + } + pTimer->m_nVecIndex = OFFSET(3) + INDEX(llExpireTime, 3); + } + + TListTimer& listTimer = m_vecTimerList[pTimer->m_nVecIndex]; + listTimer.push_back(pTimer); + pTimer->m_listIter = listTimer.end(); + --pTimer->m_listIter; +} + +void CWheelTimerModule::RemoveTimer(CWheelTimer* pTimer) +{ + TListTimer& listTimer = m_vecTimerList[pTimer->m_nVecIndex]; + listTimer.erase(pTimer->m_listIter); +} + +void CWheelTimerModule::Run() +{ + UINT64 nNow = HandlePrecision(GetCurMillisecs()); + while (m_llCheckTime <= nNow) + { + //for every tick + int index = m_llCheckTime & TVR_MASK; + if (!index && + !_Cascade(OFFSET(0), INDEX(m_llCheckTime, 0)) && + !_Cascade(OFFSET(1), INDEX(m_llCheckTime, 1)) && + !_Cascade(OFFSET(2), INDEX(m_llCheckTime, 2))) + { + _Cascade(OFFSET(3), INDEX(m_llCheckTime, 3)); + } + + + ++m_llCheckTime; + + TListTimer& listTimer = m_vecTimerList[index]; + TListTimer listTmp; + listTmp.splice(listTmp.end(), listTimer); + for (auto itr = listTmp.begin(); itr != listTmp.end(); ++itr) + { + auto* pTimer = *itr; + if (pTimer != nullptr) + { + pTimer->OnTrigger(nNow); + } + } + } +} + +int CWheelTimerModule::_Cascade(int nOffset, int nIndex) +{ + TListTimer& listTimer = m_vecTimerList[nOffset + nIndex]; + TListTimer listTemp; + listTemp.splice(listTemp.end(), listTimer); + + for (auto itr = listTemp.begin(); itr != listTemp.end(); ++itr) + { + auto* pTimer = *itr; + if (pTimer != nullptr) + { + AddTimer(pTimer); + } + } + + return nIndex; +} + +UINT64 CWheelTimerModule::HandlePrecision(const UINT64 nSrcTime) +{ + return nSrcTime / WHEEL_TIMER_MIN_PRECISION; +} + +UINT64 CWheelTimerModule::GetCurMillisecs() +{ + auto llCurTime = CSysTime::Instance()->GetNowMliTime(); + return llCurTime; +} + +ITimerMgr::~ITimerMgr() +{ + for (auto it : m_mapTimer) + { + if (it.second != nullptr) + { + it.second->Stop(); + CTimerFactory::Instance()->ReleaseCTimer(it.second); + } + } + m_mapTimer.clear(); +} + +void ITimerMgr::SetTimer(unsigned int nId, int nInterval, int nDelay ,ETimerType eTimeType) +{ + if (IsTimerExist(nId)) + { + EXLOG_DEBUG << "[RyzTimer]Timer Has Existed, Not Repeat Add, nId:" << nId; + return; + } + + CWheelTimer* pTimer = CTimerFactory::Instance()->CreateCTimer(); + if (nullptr == pTimer) + { + return; + } + + m_mapTimer[nId] = pTimer; + pTimer->Start(this, nId, nInterval, nDelay, eTimeType); + EXLOG_DEBUG << "[RyzTimer]Add Timer nId:" << nId << ",nInterval:" << nInterval << ",nDelay:" << nDelay << ",eTimeType:" << eTimeType; +} + +void ITimerMgr::KillTimer(unsigned int nId) +{ + auto it = m_mapTimer.find(nId); + if (it != m_mapTimer.end()) + { + // 释放后 在TimerManager中 不会再执行 不需要做其他的操作 + it->second->Stop(); + CTimerFactory::Instance()->ReleaseCTimer(it->second); + m_mapTimer.erase(it); + } +} + +bool ITimerMgr::IsTimerExist(unsigned int nId) +{ + return m_mapTimer.find(nId) != m_mapTimer.end(); +} + +CTimerFactory::CTimerFactory() +{ + m_oCTimerPool.Init(32, 8); +} + +CTimerFactory::~CTimerFactory() +{ +} + +CWheelTimer * CTimerFactory::CreateCTimer() +{ + CWheelTimer* pTimer = m_oCTimerPool.FetchObj(); + return pTimer; +} + +void CTimerFactory::ReleaseCTimer(CWheelTimer* pTimer) +{ + if (nullptr != pTimer) + { + m_oCTimerPool.ReleaseObj(pTimer); + } +} + + +``` \ No newline at end of file diff --git "a/source/_posts/\347\275\221\346\230\223\344\272\222\345\250\261\351\235\242\350\257\225\346\200\273\347\273\223.md" "b/source/_posts/\347\275\221\346\230\223\344\272\222\345\250\261\351\235\242\350\257\225\346\200\273\347\273\223.md" new file mode 100644 index 0000000..19acf92 --- /dev/null +++ "b/source/_posts/\347\275\221\346\230\223\344\272\222\345\250\261\351\235\242\350\257\225\346\200\273\347\273\223.md" @@ -0,0 +1,179 @@ +--- +title: 网易互娱面试总结 +date: 2020-01-23 8:12:45 +tags: 面试 +--- + +## 现场二面技术总监面试 +这个人看起来挺和善的,实际上还是有一套的,让你写代码,然后眼睛编译给你指出错误,然后修改,然后他就去玩手机了... +1. linux env multi-thread gdb debug 多线程调试 (回答对一半) + 首先介绍下基本命令 + - `info threads` 显示当前可以调试的所有线程,gdb会为每一个线程分配一个唯一ID,利用这个唯一Id可以切换到这个线程上下文环境中,并且前面有`*`标识的是当前调试的线程 + ![](http:\\genge.cc\wp-content\uploads\2018\09\cfd3fadfb7b7fd387f68feb079e1a99a.png) + - `thread ID` 切换调试的线程为指定ID的线程。这个Id是gdb为每个线程分配的,并不是操作系统分配的PID,可以通过`info threads`命令查看 + - `break xx.cpp:123 thread all` 在所有线程中相应的行上设置断点 + - `break apply ID1 ID2 cmd` 让一个或者多个线程执行gdb命令cmd + - `break apply all cmd` 让所有被调试线程执行GDB命令command + - `set print thread-events` **设置线程创建提醒** 当运行过程总产生新线程的时候会打印 + - `set scheduler-locking off|on|step` 这个是重点,经常被问到,在使用step或者continue命令调试当前被调试线程的时候,其他线程也是同时执行的,怎么只让被调试程序执行呢?通过这个命令就可以实现这个需求。 + - off 不锁定任何线程,也就是所有线程都执行,这是默认值。 + - on 只有当前被调试程序会执行。. + - step 在单步的时候,除了next过一个函数的情况(熟悉情况的人可能知道,这其实是一个设置断点然后continue的行为)以外,只有当前线程会执行。该模式是对single-stepping模式的优化。此模式会阻止其他线程在当前线程单步调试时,抢占当前线。因此调试的焦点不会被以外的改变。其他线程不可能抢占当前的调试线程。其他线程只有下列情况下会重新获得运行的机会:当你‘next’一个函数调用的时候。当你使用诸如‘continue’、‘until‘、’finish‘命令的时候。其他线程遇到设置好的断点的时候。 + +- 调试C++或者C的宏 + 在编译程序的时候,加上`-ggdb3`参数,这样就可以调试宏 + - `info macro –` 你可以查看这个宏在哪些文件里被引用了,以及宏定义是什么样的。 + - `macro – `你可以查看宏展开的样子。 +- 关联源文件 + - 如果在编译情况下加上`-g`参数,那么就可以包含debug信息,否者gdb找不到符号表 + - 可以使用`directory`命令来设置源文件的目录 + ![](http:\\genge.cc\wp-content\uploads\2018\09\942cbce8346b6e1ef65a32ea8c1919a9.png) +- 条件断点 + 基本语法` break [where] if [condition]` 尤其是在一个循环或递归中,或是要监视某个变量。注意,这个设置是在GDB中的,只不过每经过那个断点时GDB会帮你检查一下条件是否满足。 +- 添加参数 + 1. gdb命令行的 –args 参数 + 2. gdb环境中 set args命令。 +- 设置变量 + 1. 可以直接使用set命令 设置上下文环境变量值,可以模拟一些很难在测试中出现的情况,以防未来程序出错 + 2. 声明变量,然后使用,语法为`$name = 1` +- X命令 + 平时我们一般使用p命令打印参数值,但是这个命令必须指定变量名,不知道变量名的时候,我们可以使用X命令 + 1. x\x 以十六进制输出 + 2. x\d 以十进制输出 + 3. x\c 以单字符输出 + 4. x\i 反汇编 – 通常,我们会使用 x\10i $ip-20 来查看当前的汇编($ip是指令寄存器) + 5. x\s 以字符串输出 +- command命令 把一组命令录制下来打包成‘宏’ + ![](http:\\genge.cc\wp-content\uploads\2018\09\1bc671b5484282a324ff2d3b68863cb7.png) + +2. 循环队列判空 (OK) + 有三种方式处理这种问题 + 1. 队列Queue结构中保存一个计数器count表示当前队列元素个数(最简单粗暴),但count等于队列cap的时候就队列满,count为0的时候队列空 + 2. **少用一个元素空间**,约定以“队列头指针front在队尾指针rear的下一个位置上”作为队列“满”状态的标志。这种方法比较常用,但是面试官不让用.... front指向队首元素,rear指向队尾元素的下一个元素。即: + - 队空时: front=rear + - 队满时: (rear+1)%maxsize=front + 3. 还有一个比较取巧的办法,优化第一种方案:使用一个状态flag变量,初始值为0,但入队成功置flag = 1,当出队成功设置flag = 0。我们可以使用 `front == rear && flag` 表示队列满(在入队操作之后导致front=rear),可以使用`front == rear && !flag`表示队列空(出队后导致f==r,显然是队列空) + +3. 单向队列反转 (OK) 很简单 + +```cpp + +struct Node +{ + int data; + Node * next; +}; + +Node* reverse_list(Node* head) +{ + if(head == nullptr) return head; + + Node* node = nullptr; + node = head; + head = head->next; + node = nullptr; + + while(head != nullptr) + { + Node* next = head->next; + head->next = node; + node = head; + head = next; + } + + return node; +} + +``` + +## 现场面一面回忆总结 +估计是小组长之类的面试官吧,去之前我还特意看下自己的衣装是否整洁,这个面试官感觉是从`工地`上回来的,衣服上很脏,典型程序员面孔,他问的问题算是比较全面 +操作系统(linux)、数据库(mysql)、算法、数据结构、计算机网络基础、网络编程、语言基础(C++语言)、并发、以及具体业务设计,还有项目基本介绍,游戏服务器架构简单介绍 + +常见模块实现 +1. 定时器实现方式目前应用比较多的有时间轮和最小堆方式 , 优缺点其实就是算法复杂度: + 实现方式 StartTimer StopTimer PerTickBookkeeping + 基于链表 O(1) O(n) O(n) + 基于排序链表 O(n) O(1) O(1) + 基于最小堆 O(lgn) O(1) O(1) + 基于时间轮 O(1) O(1) O(1) +https:\\www.ibm.com\developerworks\cn\linux\l-cn-timers\ + +2. 斐波那契数 多种实现 + 1. 递归 最简单 粗暴 效率最低 存在大量重复计算 + 2. 循环叠加 算法复杂度为O(n) + 3. 申请额外数组保存结果 去除重复计算 空间换时间 + 4. 利用数学公式推导,矩阵相乘推导公式,算法复杂度为O(logn) 效率最高 + {f(n), f(n-1), f(n-1), f(n-2)} ={1, 1, 1,0}n-1 + {f(n), f(n-1), f(n-1), f(n-2)} + 详情见 https:\\www.cnblogs.com\python27\archive\2011\11\25\2261980.html + 5. 通项公式 这个实在是牛逼 一个公式搞定... 不是数学系 这些方法确实想不出 只能记忆 + ```cpp + int Fibonacci(int n) + { + double tmp=sqrt(double(5)); + return int((pow((1+tmp)\2,n)-pow((1-tmp)\2,n))\tmp); + } + + ``` + 6. 定理 + ```cpp + int Fibonacci(int n) + { + if(n==0)return 0; + if(n==1||n==2)return 1; \\当n>=3时,n>n\2+1 + int x=Fibonacci(n\2); + int y=Fibonacci(n\2+1); + if(n&1)return x*x+y*y; + return x*(2*y-x); + } + ``` +3. 敏感字过滤算法 + 1. 正则匹配 面试官一般不会让你用这个 因为要匹配的内容太多 写正则表达式就很烦... 效率还不高 KMP算法 太慢 不能用 + 2. 自己当时想到的一张方法为字典树TrieTree 这个方法太耗空间 时间复杂度为O(key_max_len) 很多生产环境确实是用这个实现的,别名有限状态机 DFA:DFA即Deterministic Finite Automaton,也就是确定有穷自动机 + 3. 其他什么优化算法 其实也不用 + +4. 数据库 + 1. 数据库事务特性 + ACID 原子性 一致性 隔离性 持久性dura + + 2. 事务隔离级别 + + 隔离级别 脏读(Dirty Read) 不可重复读(NonRepeatable Read) 幻读(Phantom Read) + + 未提交读(Read uncommitted) 可能 可能 可能 + + 已提交读(Read committed) 不可能 可能 可能 + + 可重复读(Repeatable read) 不可能 不可能 可能 + + 可串行化(Serializable ) 不可能 不可能 不可能 + + ·未提交读(Read Uncommitted):允许脏读,也就是可能读取到其他会话中未提交事务修改的数据(事务之间关系) + ·提交读(Read Committed):只能读取到已经提交的数据。**Oracle等多数数据库默认都是该级别** (不重复读) (事务之间关系) + ·可重复读(Repeated Read):可重复读。在同一个事务内的查询都是事务开始时刻一致的,**InnoDB默认级别**。在SQL标准中,该隔离级别消除了不可重复读,但是还存在幻象读 (事务内部) + ·串行读(Serializable):完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞 + 可重复读和提交读是矛盾的。在同一个事务里,如果保证了可重复读,就会看不到其他事务的提交,违背了提交读;如果保证了提交读,就会导致前后两次读到的结果不一致,违背了可重复读。 + 可以这么讲,InnoDB提供了这样的机制,在默认的可重复读的隔离级别里,可以使用加锁读去查询最新的数据(提交读)。 + MySQL InnoDB的可重复读并不保证避免幻读,需要应用使用加锁读来保证。而这个加锁度使用到的机制就是next-key locks。 + 总结:[MySQL 四种事务隔离级的说明 - jyzhou - 博客园](http:\\www.cnblogs.com\zhoujinyi\p\3437475.html) + 四个级别逐渐增强,每个级别解决一个问题。事务级别越高,性能越差,大多数环境read committed 可以用.记住4个隔离级别的特点(上面的例子); + 3. 事务实现原理 + https:\\draveness.me\mysql-transaction 介绍事务ACID的火滚日志实现 https:\\www.cnblogs.com\wy123\p\8365234.html 具体日志格式 + 4. innodb和myisam存储引擎的区别 https:\\blog.csdn.net\xifeijian\article\details\20316775 + - MyISAM类型不支持事务处理等高级处理,而InnoDB类型支持,MyISAM类型的表强调的是性能,其执行数度比InnoDB类型更快,但是不提供事务支持,而InnoDB提供事务支持以及外部键等高级数据库功能。 + - InnoDB不支持FULLTEXT类型的索引。 + - InnoDB 中不保存表的具体行数,也就是说,执行select count(*) from table时,InnoDB要扫描一遍整个表来计算有多少行,但是MyISAM只要简单的读出保存好的行数即可。注意的是,当count(*)语句包含 where条件时,两种表的操作是一样的。 + - 对于AUTO_INCREMENT类型的字段,InnoDB中必须包含只有该字段的索引,但是在MyISAM表中,可以和其他字段一起建立联合索引。 + - 细节可以看链接 https:\\blog.csdn.net\xifeijian\article\details\20316775和下面一个介绍 + - https:\\www.jianshu.com\p\a957b18ba40d + 5. 对于like查询啥时候会用到索引 http:\\thephper.com\?p=142 + > like 不能用索引? 这个确实不知道 难受 + 尽量减少like,但不是绝对不可用,”xxxx%” 是可以用到索引的, + 想象一下,你在看一本成语词典,目录是按成语拼音顺序建立,查询需求是,你想找以 “一”字开头的成语(”一%“),和你想找包含一字的成语(“%一%”) + 除了like,以下操作符也可用到索引: + <,<=,=,>,>=,BETWEEN,IN + <>,not in ,!=则不行 + + 6. 索引类型 + https:\\segmentfault.com\q\1010000003832312 http:\\blog.codinglabs.org\articles\theory-of-mysql-index.html \ No newline at end of file diff --git a/source/about/index.md b/source/about/index.md new file mode 100644 index 0000000..61fb767 --- /dev/null +++ b/source/about/index.md @@ -0,0 +1,6 @@ +--- +title: 关于 +date: 2020-01-23 16:09:55 +--- + +Hello World, 字节跳动搬砖五年多! \ No newline at end of file diff --git a/source/friends/index.md b/source/friends/index.md new file mode 100644 index 0000000..fd597b1 --- /dev/null +++ b/source/friends/index.md @@ -0,0 +1,6 @@ +--- +title: friends +date: 2023-03-27 19:34:33 +type: "friends" +layout: "friends" +--- diff --git a/source/tags/index.md b/source/tags/index.md new file mode 100644 index 0000000..0dcf934 --- /dev/null +++ b/source/tags/index.md @@ -0,0 +1,6 @@ +--- +title: tags +date: 2023-03-27 19:35:18 +type: "tags" +layout: "tags" +--- diff --git a/source/timeline/index.md b/source/timeline/index.md new file mode 100644 index 0000000..6aae593 --- /dev/null +++ b/source/timeline/index.md @@ -0,0 +1,5 @@ +--- +title: 胡说八道 +date: 2020-01-31 17:42:02 +layout: timeline +--- \ No newline at end of file diff --git a/styles/main.css b/styles/main.css deleted file mode 100644 index ef9bc01..0000000 --- a/styles/main.css +++ /dev/null @@ -1,877 +0,0 @@ -:root { - --theme-first: #ffffff; - --theme-second: #f8f9fa; - --content-first: #343a40; - --content-second: #adb5bd; - --content-code: #f9f2f4; - --border: #dfe2e5; - --accent-first: #bfbfbf; - --accent-second: #8b959f66; -} -.gt-bg-theme-color-first { - background: var(--theme-first); -} -.gt-bg-theme-color-second { - background: var(--theme-second); -} -.gt-bg-content-color-first { - background: var(--content-first); -} -.gt-bg-content-color-second { - background: var(--content-second); -} -.gt-bg-accent-color-first { - background: var(--accent-first); -} -.gt-bg-accent-color-second { - background: var(--accent-second); -} -.gt-c-theme-color-first { - color: var(--theme-first); -} -.gt-c-theme-color-second { - color: var(--theme-second); -} -.gt-c-content-color-first { - color: var(--content-first); -} -.gt-c-content-color-second { - color: var(--content-second); -} -.gt-c-accent-color-first { - color: var(--accent-first); -} -.gt-c-accent-color-second { - color: var(--accent-second); -} -*, -*:before, -*:after { - margin: 0; - padding: 0; -} -*:focus { - outline-width: 0px; -} -*:focus-visible { - outline-width: 1px; -} -html { - font-size: 58%; -} -body { - font-family: 'Open Sans', 'PingFang SC', -apple-system, BlinkMacSystemFont, opensans, Optima, 'Microsoft Yahei', sans-serif; - font-size: 16px; - letter-spacing: 1.0382px; -} -button { - outline: none !important; -} -a { - text-decoration: none !important; - transition: all 0.3s; -} -body, -div, -a, -p, -ul, -li, -ol, -h1, -h2, -h3, -h4, -h5, -h6, -table, -tr, -td { - box-sizing: border-box; - margin: 0; - padding: 0; -} -ol li { - list-style: decimal; - font-size: 16px; -} -ul li { - list-style: disc; - font-size: 16px; -} -hr { - margin: 1rem 0px; - border: 0; - border-top: 1px solid rgba(0, 0, 0, 0.1); -} -.main { - max-width: 100%; - min-height: 100vh; - margin: 0 auto; -} -.main .main-content { - flex: 1; - display: flex; - min-height: 100vh; - flex-direction: column; - justify-content: space-between; -} -.navbar { - padding: 38px 96px !important; -} -.navbar .navbar-brand { - display: flex; - flex-direction: row; - align-items: center; -} -.navbar .navbar-brand .user-avatar { - width: 40px; - height: 40px; - border-radius: 50%; -} -.navbar .navbar-brand .site-name { - font-size: 28px !important; - font-weight: bold; - margin-left: 16px; -} -.navbar .navbar-nav { - width: 100%; - justify-content: flex-end; -} -.navbar .navbar-nav .nav-item { - padding: 14px 26px; -} -.navbar .navbar-nav .nav-item a { - font-size: 15px; - flex-shrink: 0; -} -@media (max-width: 992px) { - .navbar { - padding: 30px 30px 0 30px !important; - margin-bottom: 30px; - } - .navbar .navbar-brand .user-avatar { - width: 31px; - height: 31px; - border-radius: 50%; - } - .navbar .navbar-brand .site-name { - font-size: 21px !important; - font-weight: bold; - } - .navbar .nav-item { - padding: 16px 24px; - } - .navbar .nav-item:first-child { - margin-top: 35px; - } -} -@media (min-width: 992px) { - .navbar { - max-width: 950px; - align-items: center !important; - align-self: center !important; - padding-left: 0px !important; - padding-right: 0px !important; - min-width: 950px; - margin: auto; - margin-top: 0px; - margin-bottom: 0px; - } -} -.navbar { - position: relative; - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - -ms-flex-align: center; - align-items: center; - -ms-flex-pack: justify; - justify-content: space-between; - padding: 0.5rem 1rem; -} -.navbar-brand { - display: inline-block; - padding-top: 0.3125rem; - padding-bottom: 0.3125rem; - margin-right: 1rem; - font-size: 1.25rem; - line-height: inherit; - white-space: nowrap; -} -.navbar-brand:hover, -.navbar-brand:focus { - text-decoration: none; -} -.navbar-nav { - display: -ms-flexbox; - display: flex; - -ms-flex-direction: column; - flex-direction: column; - padding-left: 0; - margin-bottom: 0; - list-style: none; -} -.navbar-collapse { - -ms-flex-preferred-size: 100%; - flex-basis: 100%; - -ms-flex-positive: 1; - flex-grow: 1; - -ms-flex-align: center; - align-items: center; -} -.navbar-toggler { - padding: 0.25rem 0.75rem; - font-size: 1.25rem; - line-height: 1; - background-color: transparent; - border: 1px solid transparent; - border-radius: 0.25rem; -} -.navbar-toggler:hover, -.navbar-toggler:focus { - text-decoration: none; -} -@media (min-width: 992px) { - .navbar-expand-lg { - -ms-flex-flow: row nowrap; - flex-flow: row nowrap; - -ms-flex-pack: start; - justify-content: flex-start; - } - .navbar-expand-lg .navbar-nav { - -ms-flex-direction: row; - flex-direction: row; - } - .navbar-expand-lg .navbar-collapse { - display: -ms-flexbox !important; - display: flex !important; - -ms-flex-preferred-size: auto; - flex-basis: auto; - } - .navbar-expand-lg .navbar-toggler { - display: none; - } -} -@media (max-width: 991px) { - #navbarSupportedContent { - display: none; - } -} -.site-footer { - font-size: 12px; - padding: 24px; - max-width: 1000px; - min-width: 1000px; - align-self: center; - margin: 0 auto; -} -.site-footer a { - color: var(--content-first) !important; -} -@media (max-width: 992px) { - .site-footer { - display: none; - } -} -.post-list-container { - display: flex; - flex-direction: column; - align-items: center; -} -.post-list-container .post-inner { - min-width: 1000px; - max-width: 1000px; -} -@media (max-width: 992px) { - .post-list-container .post-inner { - min-width: 0; - width: 100%; - margin-top: 16px; - } - .post-list-container .post-inner .post { - margin: 12px 16px 12px 16px; - padding: 18px 22px 18px 22px; - } - .post-list-container .post-inner .post .post-left .post-title { - font-size: 17px; - font-weight: bold; - } - .post-list-container .post-inner .post .post-left .post-abstract { - margin-top: 10px; - } - .post-list-container .post-inner .post .post-left .post-info { - margin-top: 10px; - } - .post-list-container .post-inner .post .post-feature-image { - width: 0; - height: 0; - flex-basis: 0; - flex-shrink: 0; - margin-left: 0; - border-radius: 2px; - overflow: hidden; - background-size: cover; - background-position: center; - } -} -.post-list-container .post-inner .post { - margin: 30px; - border-radius: 4px; - padding: 28px; - display: flex; - flex-direction: row; - justify-content: space-between; - align-items: center; - transition: all 0.3s; -} -.post-list-container .post-inner .post .post-left { - width: 100%; - display: flex; - flex-direction: column; -} -.post-list-container .post-inner .post .post-left code { - color: #bfbfbf; - font-family: Consolas; - background: none !important; - padding: 0; - margin: 0; -} -.post-list-container .post-inner .post .post-left .sticky-top-flag { - color: white; - padding: 3px 10px; - font-size: 12px; - border-radius: 3px; - margin-right: 10px; - vertical-align: middle; -} -.post-list-container .post-inner .post .post-left .post-title { - font-size: 22px; - font-weight: normal; - vertical-align: middle; -} -.post-list-container .post-inner .post .post-left .post-abstract { - width: 100%; - margin-top: 20px; - line-height: 1.5; -} -.post-list-container .post-inner .post .post-left .post-abstract * { - font-size: 13px; - margin-bottom: 0; -} -.post-list-container .post-inner .post .post-left .post-info { - margin-top: 20px; - font-size: 12px; - display: flex; - justify-content: space-between; -} -.post-list-container .post-inner .post .post-left .post-info .post-tag, -.post-list-container .post-inner .post .post-left .post-info .post-category { - margin: 0 10px; - color: var(--accent-first); -} -.post-list-container .post-inner .post .post-left .post-info a { - color: var(--accent-first); -} -.post-list-container .post-inner .post:hover { - transform: scale(1.012); -} -.post-list-container .post-inner .post .post-feature-image { - width: 200px; - height: 100px; - flex-basis: 200px; - flex-shrink: 0; - margin-left: 20px; - border-radius: 2px; - overflow: hidden; - background-size: cover; - background-position: center; -} -.pagination-container { - padding: 24px 32px 32px; - align-self: center; -} -.pagination-container .prev-page { - margin: 0 16px; - font-size: 14px; -} -.pagination-container .next-page { - margin: 0 16px; - font-size: 14px; -} -.post-container { - display: flex; - flex-direction: column; - align-items: center; - min-height: 100%; - flex: 1; -} -.post-container .post-detail { - flex: 1; - padding: 24px 32px; - width: 1000px; - border-radius: 5px; -} -.post-container .post-detail .post-title { - font-size: 26px; - text-align: center; - padding: 24px; - font-weight: normal; -} -.post-container .post-detail .post-info { - text-align: center; - font-size: 12px; - padding-bottom: 24px; -} -.post-container .post-detail .post-info .post-tag { - padding: 8px; -} -.post-container .post-detail .post-toc { - margin-bottom: 30px; -} -.post-container .post-detail .post-toc h2 { - display: inline-block; - background: linear-gradient(180deg, transparent 75%, rgba(139, 149, 159, 0.4) 0) !important; -} -.post-container .post-detail .post-toc ul { - margin: 0px; -} -.post-container .post-detail .post-toc .markdownIt-TOC { - padding: 0px; -} -.post-container .post-detail .post-toc .markdownIt-TOC li { - list-style: none; - margin: 0px; -} -.post-container .post-detail .post-toc .markdownIt-TOC li ul li { - list-style-type: disc; - margin: 0px; -} -.post-container .post-detail .post-toc .markdownIt-TOC li ul li a { - color: var(--content-first) !important; -} -.post-container .post-detail .post-content { - margin-top: 30px; -} -.post-container .post-detail .post-content h1, -.post-container .post-detail .post-content h2, -.post-container .post-detail .post-content h3, -.post-container .post-detail .post-content h4, -.post-container .post-detail .post-content h5, -.post-container .post-detail .post-content h6 { - display: table !important; -} -.post-container .post-detail .post-content h2, -.post-container .post-detail .post-content h3 { - display: inline-block; - color: var(--content-first) !important; - position: relative !important; - background: linear-gradient(180deg, transparent 75%, var(--accent-second) 0) !important; -} -@media (max-width: 992px) { - .post-container .post-detail { - width: 100%; - margin-top: 20px; - } - .post-container .post-detail .post-title { - font-size: 20px; - font-weight: bold; - } -} -.post-container details { - background: var(--accent-second); - border-radius: 5px; - padding: 10px 20px; -} -.post-container details summary { - user-select: none; - font-size: 14px; - font-weight: bold; - padding: 10px 20px; - border-radius: 5px; - background: var(--accent-second); - margin: -10px -20px; -} -.post-container details[open] summary { - border-radius: 5px 5px 0px 0px; - margin: -10px -20px 15px -20px; -} -.post-container details p { - font-size: 14px; -} -.gt-a-link { - color: var(--content-first) !important; - position: relative !important; - background: linear-gradient(180deg, transparent 75%, #4dabf766 0) !important; -} -.gt-post-content { - word-break: normal; - word-wrap: break-word; -} -.gt-post-content a, -.gt-post-content a code { - color: var(--accent-first) !important; - transition: all 0.3s; -} -.gt-post-content a:hover { - text-decoration: underline !important; -} -.gt-post-content .post-toc + hr { - margin-bottom: calc(1rem + 18px); -} -.gt-post-content img { - display: block; - max-width: 100%; - border-radius: 3px; - margin: 18px auto; -} -.gt-post-content p { - line-height: 1.725; - margin-bottom: 18px; - font-size: 16px; - letter-spacing: 1.0382px; -} -.gt-post-content pre { - margin-bottom: 18px; -} -.gt-post-content blockquote { - padding: 16px; - border: 0; - border-left: 4px; - border-style: solid; - margin-bottom: 16px; - background: var(--accent-second) !important; - border-color: var(--accent-first) !important; -} -.gt-post-content blockquote p { - margin-bottom: 0; -} -.gt-post-content table { - border-collapse: collapse; - margin: 1rem 0; - overflow-x: auto; - display: table; - width: 100%; -} -.gt-post-content table thead th { - text-align: left; -} -.gt-post-content table tr { - border-top: 1px solid var(--border); -} -.gt-post-content table td, -.gt-post-content table th { - border: 1px solid var(--border); - padding: 0.6em 1em; -} -.gt-post-content ul, -.gt-post-content ol { - padding-left: 20px; - line-height: 1.725; - margin-bottom: 16px; -} -.gt-post-content h1, -.gt-post-content h2, -.gt-post-content h3, -.gt-post-content h4, -.gt-post-content h5, -.gt-post-content h6 { - margin-bottom: 18px; - padding-top: 14px; - font-weight: bold; -} -.gt-post-content h1 { - font-size: 28px; -} -.gt-post-content h2 { - font-size: 24px; -} -.gt-post-content h3 { - font-size: 20px; -} -.gt-post-content h4 { - font-size: 18px; -} -.gt-post-content h5 { - font-size: 16px; -} -.gt-post-content h6 { - font-size: 14px; -} -.gt-post-content pre { - margin-bottom: 18px; - margin-right: 32px; -} -.gt-post-content pre > code { - letter-spacing: 0px; - font-size: 14px; - font-family: 'Roboto Mono', Consolas, Menlo, Monaco, 'Source Code Pro', 'Courier New', monospace; - padding: 1em; - border-radius: 5px; - line-height: 1.375; - position: relative; - width: 100%; - overflow: scroll; - display: block; -} -.gt-post-content p code, -.gt-post-content li code, -.gt-post-content table code { - letter-spacing: 0px; - color: var(--content-first); - font-family: 'Roboto Mono', Consolas, Menlo, Monaco, 'Source Code Pro', 'Courier New', monospace; - padding: 0.2em 0.4em; - margin: 0; - font-size: 85%; - background-color: #1b1f230d; - border-radius: 6px; - word-break: break-all; -} -.post-detail { - margin-top: 0px !important; - padding-top: 0px !important; -} -.next-prev-post { - text-align: center; - padding: 24px 32px; -} -.next-prev-post .next-post .next { - margin-bottom: 24px; - font-size: 14px; -} -.next-prev-post .next-post .post-title { - font-size: 16px; - font-weight: bold; -} -.next-prev-post .prev-post .prev { - margin-bottom: 24px; - font-size: 14px; -} -.next-prev-post .prev-post .post-title { - font-size: 16px; - font-weight: bold; -} -.archives-container { - padding: 32px; - display: flex; - flex-direction: column; - margin-left: 10%; - margin-right: 10%; - align-self: center; - max-width: 1000px; - min-width: 1000px; -} -.archives-container .year { - font-size: 34px; - font-weight: bold; - margin-top: 16px; - margin-bottom: 16px; - align-self: start; -} -.archives-container .post { - padding: 16px 0; -} -.archives-container .post .post-title { - font-size: 16px; -} -.archives-container .post .post-title small { - font-size: 80%; -} -.archives-container .post .post-title .archives-post-title { - display: inline-block; - margin-left: 20px; -} -.archives-container a:hover { - text-decoration: none !important; -} -@media (max-width: 600px) { - .archives-container { - padding: 16px !important; - } -} -@media (max-width: 992px) { - .archives-container { - min-width: 0px !important; - } -} -.current-tag-container .title { - text-align: center; - font-size: 18px; - margin-bottom: 24px; -} -@media (max-width: 992px) { - .current-tag-container .title { - margin-top: 50px; - } -} -.tags-container { - padding: 32px 32px; - flex: 1; - display: flex; - flex-direction: row; - justify-content: center; - align-items: center; - flex-wrap: wrap; - max-width: 1000px; - align-self: center; -} -.tags-container .tag { - font-size: 15px; - margin: 5px 15px; -} -.friend-box { - float: left; - max-width: calc(50% - 20px); - min-width: calc(50% - 20px); - margin: 15px 10px; - background-color: rgba(20, 153, 196, 0.125); - padding: 15px; - border-radius: 15px; -} -.friend-box img.friend-avatar { - width: 70px; - height: 70px; - border-radius: 50% !important; - float: left; - margin: 0 15px 0 0 !important; -} -.friend-box .friend-info { - height: 70px; - overflow: hidden; - line-height: 24px; - padding-left: 30px; - font-size: 14px; -} -.friend-box .friend-info a { - text-decoration: none !important; - font-size: 18px; - background: linear-gradient(180deg, transparent 75%, rgba(116, 192, 252, 0.4) 0); - margin-bottom: 10px; -} -.friend-box .friend-info .friend-info-description { - margin-top: 10px; -} -@media (max-width: 465px) { - .friend-box .friend-info .friend-info-description { - display: none; - } -} -@media (max-width: 993px) { - .friend-box { - max-width: 90% !important; - min-width: 90% !important; - } -} -#disqus_thread { - padding: 24px 32px; - width: 80%; - max-width: 1000px; -} -@media (max-width: 992px) { - #disqus_thread { - padding: 24px 32px; - width: 100%; - } -} -#disqus_thread ul li { - list-style: none; -} -#dsqjs { - font-family: 'PingFang SC', -apple-system, BlinkMacSystemFont, opensans, Optima, 'Microsoft Yahei', sans-serif; -} -#gitalk-container { - width: 80%; - max-width: 1000px; -} -@media (max-width: 992px) { - #gitalk-container { - width: 100%; - } -} -#vcomments { - width: 80%; - max-width: 1000px; -} -@media (max-width: 992px) { - #vcomments { - width: 100%; - } -} -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - color: #383a42; - background: #eeeeee; -} -.hljs .hljs-comment, -.hljs .hljs-quote { - color: #a0a1a7; - font-style: italic; -} -.hljs .hljs-doctag, -.hljs .hljs-formula, -.hljs .hljs-keyword { - color: #a626a4; -} -.hljs .hljs-deletion, -.hljs .hljs-name, -.hljs .hljs-section, -.hljs .hljs-selector-tag, -.hljs .hljs-subst { - color: #e45649; -} -.hljs .hljs-literal { - color: #0184bb; -} -.hljs .hljs-addition, -.hljs .hljs-attribute, -.hljs .hljs-meta-string, -.hljs .hljs-regexp, -.hljs .hljs-string { - color: #50a14f; -} -.hljs .hljs-built_in, -.hljs .hljs-class .title { - color: #c18401; -} -.hljs .hljs-attr, -.hljs .hljs-number, -.hljs .hljs-selector-attr, -.hljs .hljs-selector-class, -.hljs .hljs-selector-pseudo, -.hljs .hljs-template-variable, -.hljs .hljs-type, -.hljs .hljs-variable { - color: #986801; -} -.hljs .hljs-bullet, -.hljs .hljs-link, -.hljs .hljs-meta, -.hljs .hljs-selector-id, -.hljs .hljs-symbol, -.hljs .hljs-title { - color: #4078f2; -} -.hljs .hljs-emphasis { - font-style: italic; -} -.hljs .hljs-strong { - font-weight: 700; -} -.hljs .hljs-link { - text-decoration: underline; -} -.hljs::-webkit-scrollbar { - width: 8px; - height: 6px; -} -.hljs::-webkit-scrollbar-thumb { - border-radius: 4px; - background-color: #cbcbcb !important; -} -.hljs::-webkit-scrollbar-track-piece, -.hljs::-webkit-scrollbar-corner { - background: #eeeeee !important; -} diff --git a/tags/Redis/index.html b/tags/Redis/index.html deleted file mode 100644 index 8aca698..0000000 --- a/tags/Redis/index.html +++ /dev/null @@ -1,151 +0,0 @@ - - - - - - - -Genge 随笔 - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - -
-

含有 Redis 标签的文章

-
-
-
- - - - - -
-
- -
- - -
- - - -
-
- - \ No newline at end of file diff --git a/tags/flag/index.html b/tags/flag/index.html deleted file mode 100644 index 1ea5ca3..0000000 --- a/tags/flag/index.html +++ /dev/null @@ -1,151 +0,0 @@ - - - - - - - -Genge 随笔 - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - -
-

含有 flag 标签的文章

-
-
-
- - - - - -
-
- -
- - -
- - - -
-
- - \ No newline at end of file diff --git a/tags/git/index.html b/tags/git/index.html deleted file mode 100644 index 1f41312..0000000 --- a/tags/git/index.html +++ /dev/null @@ -1,151 +0,0 @@ - - - - - - - -Genge 随笔 - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - -
-

含有 git 标签的文章

-
-
-
- - - - - -
-
- -
- - -
- - - -
-
- - \ No newline at end of file diff --git a/tags/index.html b/tags/index.html deleted file mode 100644 index a1237f0..0000000 --- a/tags/index.html +++ /dev/null @@ -1,147 +0,0 @@ - - - - - - - -tags - Genge 随笔 - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - -
- - #flag - - #linux - - #网络 - - #uuid - - #ringbuff - - #rank - - #git - - #timer - - #面试 - - #Redis - -
- - - -
-
- - \ No newline at end of file diff --git a/tags/linux/index.html b/tags/linux/index.html deleted file mode 100644 index daeef60..0000000 --- a/tags/linux/index.html +++ /dev/null @@ -1,247 +0,0 @@ - - - - - - - -Genge 随笔 - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - -
-

含有 linux 标签的文章

-
-
-
- - - - - - - - - - - - - - - - - - - - - -
-
- -
- - -
- - - -
-
- - \ No newline at end of file diff --git a/tags/rank/index.html b/tags/rank/index.html deleted file mode 100644 index 3326d12..0000000 --- a/tags/rank/index.html +++ /dev/null @@ -1,151 +0,0 @@ - - - - - - - -Genge 随笔 - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - -
-

含有 rank 标签的文章

-
-
-
- - - - - -
-
- -
- - -
- - - -
-
- - \ No newline at end of file diff --git a/tags/ringbuff/index.html b/tags/ringbuff/index.html deleted file mode 100644 index f5a5303..0000000 --- a/tags/ringbuff/index.html +++ /dev/null @@ -1,151 +0,0 @@ - - - - - - - -Genge 随笔 - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - -
-

含有 ringbuff 标签的文章

-
-
-
- - - - - -
-
- -
- - -
- - - -
-
- - \ No newline at end of file diff --git a/tags/timer/index.html b/tags/timer/index.html deleted file mode 100644 index 214c955..0000000 --- a/tags/timer/index.html +++ /dev/null @@ -1,151 +0,0 @@ - - - - - - - -Genge 随笔 - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - -
-

含有 timer 标签的文章

-
-
-
- - - - - -
-
- -
- - -
- - - -
-
- - \ No newline at end of file diff --git a/tags/uuid/index.html b/tags/uuid/index.html deleted file mode 100644 index 1bf3d42..0000000 --- a/tags/uuid/index.html +++ /dev/null @@ -1,151 +0,0 @@ - - - - - - - -Genge 随笔 - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - -
-

含有 uuid 标签的文章

-
-
-
- - - - - -
-
- -
- - -
- - - -
-
- - \ No newline at end of file diff --git "a/tags/\347\275\221\347\273\234/index.html" "b/tags/\347\275\221\347\273\234/index.html" deleted file mode 100644 index 31cc449..0000000 --- "a/tags/\347\275\221\347\273\234/index.html" +++ /dev/null @@ -1,151 +0,0 @@ - - - - - - - -Genge 随笔 - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - -
-

含有 网络 标签的文章

-
-
-
- - - - - -
-
- -
- - -
- - - -
-
- - \ No newline at end of file diff --git "a/tags/\351\235\242\350\257\225/index.html" "b/tags/\351\235\242\350\257\225/index.html" deleted file mode 100644 index 94da982..0000000 --- "a/tags/\351\235\242\350\257\225/index.html" +++ /dev/null @@ -1,151 +0,0 @@ - - - - - - - -Genge 随笔 - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - -
-

含有 面试 标签的文章

-
-
-
- - - - - -
-
- -
- - -
- - - -
-
- - \ No newline at end of file diff --git a/themes/pure/.gitignore b/themes/pure/.gitignore new file mode 100644 index 0000000..aec42b3 --- /dev/null +++ b/themes/pure/.gitignore @@ -0,0 +1,104 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test + +# parcel-bundler cache (https://parceljs.org/) +.cache + +# Next.js build output +.next + +# Nuxt.js build / generate output +.nuxt +# dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and *not* Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port diff --git a/themes/pure/LICENSE b/themes/pure/LICENSE new file mode 100644 index 0000000..0ad25db --- /dev/null +++ b/themes/pure/LICENSE @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. diff --git a/themes/pure/README.md b/themes/pure/README.md new file mode 100644 index 0000000..4418e4d --- /dev/null +++ b/themes/pure/README.md @@ -0,0 +1,133 @@ +# Pure + +> A modern and simple theme for Hexo. + +[![Author](https://img.shields.io/badge/Author-Baoshuo-b68469.svg?style=flat-square)](https://baoshuo.ren) +[![Version](https://img.shields.io/github/v/release/renbaoshuo/hexo-theme-pure?color=%235755d9&include_prereleases&label=version&style=flat-square)](https://github.com/renbaoshuo/hexo-theme-pure/releases) +[![Hexo](https://img.shields.io/badge/hexo-4.0+-0e83cd.svg?style=flat-square)](https://hexo.io) +[![Repo Size](https://img.shields.io/github/repo-size/renbaoshuo/hexo-theme-pure?style=flat-square)](https://github.com/renbaoshuo/hexo-theme-pure) + +![image](https://user-images.githubusercontent.com/47095648/111874137-bb164800-89ce-11eb-94fc-0d7e6d17718a.png) + +English | [简体中文](/README.zh_CN.md) + +*This theme is transplanted from [imhanjie/gridea-theme-pure](https://github.com/imhanjie/gridea-theme-pure) and has been approved by the original author.* + +## Demo + ++ [Hexo Theme Unit Test of Theme Pure](https://renbaoshuo.github.io/hexo-theme-pure) + +## Installation + +### Install via GitHub + +You need to switch to the directory where the site is located before performing the following operations. + +```bash +npm install hexo-renderer-ejs hexo-renderer-less --save +git clone https://github.com/renbaoshuo/hexo-theme-pure.git themes/pure +``` + +### Install via NPM + +```bash +npm install hexo-theme-pure --save +# or use yarn: yard add hexo-theme-pure +``` + +## Settings + ++ `favicon`: Site logo displayed in browser tabs and bookmarks. (String, default: `/favicon.ico`) ++ `avatar`: Site logo displayed on the header. (String, default: `/favicon.png`) ++ `katex`: Whether to load [KaTeX](https://katex.org) CSS. (Boolean, default: `false`) ++ `force_https`: Mandatory HTTPS. (Boolean, default: `false`) ++ `check_update`: Check update before exit. (Boolean, default: `false`) ++ `menus` (Array) + - `name`: The name displayed on the navigation bar. (String) + - `link`: The link of the page to jump to. (String) + - `target`: The [target](https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/a#attr-target) of the link. (String, `_self` or `_blank`, default: `_self`) ++ `post`: Post settings + - `feature`: display post image in post list (needs to set `feature` in post, Boolean, default: `true`) ++ `disqus`: DisqusJS Settings + - `enable`: Whether to enable DisqusJS. (Boolean, default: `false`) + - `shortname`: The shortname of the Disqus site. + - `api`: Your disqus api. + - `apikey`: Your disqus api key. + - `admin`: Your disqus username. ++ `gitalk`: [Gitalk](https://github.com/gitalk/gitalk) Settings + - `enable`: Whether to enable Gitalk. (Boolean, default: `false`) + - `clientId`: Your GitHub oauth app client id + - `clientSecret`: Your GitHub oauth app client secret + - `repository`: Your GitHub comment repository + - `owner`: Your GitHub username ++ `web_analytics`: Web Analytics Settings + - `enable`: Whether to enable Web Analytics. (Boolean, default: `false`) + - `google`: Google Analytics tracker id. + - `baidu`: Baidu Analytics id. ++ `footer`: The content displayed in the footer. ++ `friends`: Friends (Array) + - `name`: The name displayed on the friends list. + - `link`: The link of the page to jump to. + - `logo`: The logo displayed on the friends list. + +## Attention + +### Code highlight Settings + +The theme has built-in highlight style of [`highlightjs`](https://highlightjs.org). Please do not enable `line_number` and `wrap` because this theme does not support them. + +Here is a configuration example: + +```yaml +highlight: + enable: true + auto_detect: true + line_number: false # This value must be `false` + wrap: false # This value must be `false` + tab_replace: ' ' # 4 spaces + hljs: true # This value must be `true` +prismjs: + enable: false +``` + +### KaTeX Settings + +It is recommended to use `hexo-renderer-markdown-it` to render Markdown and install `@neilsustc/markdown-it-katex` to render mathematical formulas. + +```bash +npm uninstall hexo-renderer-marked --save +npm install hexo-renderer-markdown-it @neilsustc/markdown-it-katex --save +``` + +Here is a configuration example: + +```yaml +# Markdown-it config +## Docs: https://github.com/celsomiranda/hexo-renderer-markdown-it/wiki +markdown: + render: + html: true + xhtmlOut: false + breaks: true + linkify: true + typographer: true + plugins: + - '@neilsustc/markdown-it-katex' + anchors: + level: 2 + collisionSuffix: 't' + permalink: false + permalinkClass: header-anchor + permalinkSymbol: '' +``` + +### DisqusJS Settings + +You must [Register a Disqus Application](https://disqus.com/api/applications/) before using [DisqusJS](https://github.com/SukkaW/DisqusJS). + +## Author + +**hexo-theme-pure** © [Baoshuo](https://github.com/renbaoshuo), Released under the [GPL-3.0](./LICENSE) License. +Authored and maintained by Baoshuo with help from [contributors](https://github.com/renbaoshuo/hexo-theme-pure/contributors). + +> [Personal Website](https://baoshuo.ren) · [Blog](https://blog.baoshuo.ren) · GitHub [@renbaoshuo](https://github.com/renbaoshuo) · Twitter [@renbaoshuo](https://twitter.com/renbaoshuo) diff --git a/themes/pure/README.zh_CN.md b/themes/pure/README.zh_CN.md new file mode 100644 index 0000000..a090126 --- /dev/null +++ b/themes/pure/README.zh_CN.md @@ -0,0 +1,135 @@ +# Pure + +> 一款为 Hexo 设计的简约主题。 + +[![Author](https://img.shields.io/badge/Author-Baoshuo-b68469.svg?style=flat-square)](https://baoshuo.ren) +[![Version](https://img.shields.io/github/v/release/renbaoshuo/hexo-theme-pure?color=%235755d9&include_prereleases&label=version&style=flat-square)](https://github.com/renbaoshuo/hexo-theme-pure/releases) +[![Hexo](https://img.shields.io/badge/hexo-4.0+-0e83cd.svg?style=flat-square)](https://hexo.io) +[![Repo Size](https://img.shields.io/github/repo-size/renbaoshuo/hexo-theme-pure?style=flat-square)](https://github.com/renbaoshuo/hexo-theme-pure) + +![image](https://user-images.githubusercontent.com/47095648/111874137-bb164800-89ce-11eb-94fc-0d7e6d17718a.png) + +[English](/README.md) | 简体中文 + +*本主题移植自 [imhanjie/gridea-theme-pure](https://github.com/imhanjie/gridea-theme-pure) ,已经过原作者同意。* + +## 示例 + ++ [宝硕博客](https://blog.baoshuo.ren)(修改版) ++ [Hexo 主题单元测试 - Pure](https://renbaoshuo.github.io/hexo-theme-pure) + +## 安装 + +### 通过 GitHub 安装 + +您需要先切换到站点所在目录,然后才能进行以下操作。 + +```bash +npm install hexo-renderer-ejs hexo-renderer-less --save +git clone https://github.com/renbaoshuo/hexo-theme-pure.git themes/pure +``` + +### 通过 NPM 安装 + +```bash +npm install hexo-theme-pure +# 或者使用 yarn:yarn add hexo-theme-pure +``` + +## 设置 + ++ `favicon`:显示在浏览器选项卡和书签中的站点徽标。(字符串,默认:`/favicon.ico`) ++ `avatar`:网站页眉处显示的 Logo 。(字符串,默认:`/favicon.png`) ++ `katex`:是否加载 [KaTeX](https://katex.org) 的 CSS 文件. (布尔值,默认:`false`) ++ `force_https`:是否强制全局 HTTPS。(布尔值,默认:`false`) ++ `check_update`:是否在程序退出前检查更新。(布尔值,默认:`false`) ++ `menus` 导航栏菜单 + - `name`:导航栏上显示的名称。(字符串) + - `link`:要跳转到的页面链接。(字符串) + - `target`:链接的 [target](https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/a#attr-target) 属性值。(字符串, `_self` 或 `_blank`,默认:`_self`) ++ `post`:文章设置 + - `feature`:在文章列表中显示文章头图(需要在文章中指定 `feature` ,布尔值,默认:`false`) ++ `disqus`:DisqusJS 设置 + - `enable`:是否开启 DisqusJS。(布尔值,默认: `false`) + - `shortname`:站点在 Disqus 中的 shortname 。 + - `api`:Disqus API endpoint 。 + - `apikey`:Disqus API Key 。 + - `admin`:管理员的 Disqus 用户名。 ++ `gitalk`: [Gitalk](https://github.com/gitalk/gitalk) 配置 + - `enable`: 是否开启 Gitalk 。 + - `clientId`: 你的 GitHub Oauth App Client Id 。 + - `clientSecret`: 你的 GitHub Oauth App Client Secret 。 + - `repository`: 你的 GitHub 评论仓库。 + - `owner`: 你的 GitHub 用户名。 ++ `web_analytics`:站点统计设置 + - `enable`:是否开启站点统计。(布尔值,默认:`false`) + - `google`:谷歌站点统计跟踪 ID 。 + - `baidu`:百度站点统计 ID 。 ++ `footer`:页脚显示的内容。 ++ `friends`:友情链接 + - `name`:站点名称。 + - `link`:链接。 + - `logo`:站点图标。 + - `description`:站点简介。 + +## 注意事项 + +### 代码高亮设置 + +本主题内置了一组针对 [`highlightjs`](https://highlightjs.org) 设计的代码高亮样式。请勿开启 `line_number` 和 `wrap` ,本主题暂时不支持这些功能。 + +配置示例: + +```yaml +highlight: + enable: true + auto_detect: true + line_number: false # This value must be `false` + wrap: false # This value must be `false` + tab_replace: ' ' # 4 spaces + hljs: true # This value must be `true` +prismjs: + enable: false +``` + +### KaTeX 数学公式 + +推荐使用 `hexo-renderer-markdown-it` 来渲染 Markdown 并安装 `@neilsustc/markdown-it-katex` 来渲染数学公式。 + +```bash +npm uninstall hexo-renderer-marked --save +npm install hexo-renderer-markdown-it @neilsustc/markdown-it-katex --save +``` + +配置示例: + +```yaml +# Markdown-it config +## Docs: https://github.com/celsomiranda/hexo-renderer-markdown-it/wiki +markdown: + render: + html: true + xhtmlOut: false + breaks: true + linkify: true + typographer: true + plugins: + - '@neilsustc/markdown-it-katex' + anchors: + level: 2 + collisionSuffix: 't' + permalink: false + permalinkClass: header-anchor + permalinkSymbol: '' +``` + +### DisqusJS 评论系统 + +您必须在使用 [DisqusJS](https://github.com/SukkaW/DisqusJS) 前 [注册一个 Disqus 应用程序](https://disqus.com/api/applications/) 。 + +## Author + +**hexo-theme-pure** © [Baoshuo](https://github.com/renbaoshuo), Released under the [GPL-3.0](./LICENSE) License. +Authored and maintained by Baoshuo with help from [contributors](https://github.com/renbaoshuo/hexo-theme-pure/contributors). + +> [Personal Website](https://baoshuo.ren) · [Blog](https://blog.baoshuo.ren) · GitHub [@renbaoshuo](https://github.com/renbaoshuo) · Twitter [@renbaoshuo](https://twitter.com/renbaoshuo) diff --git a/themes/pure/_config.example.yml b/themes/pure/_config.example.yml new file mode 100644 index 0000000..18b4b80 --- /dev/null +++ b/themes/pure/_config.example.yml @@ -0,0 +1,103 @@ +# Hexo Configuration +## Docs: https://hexo.io/docs/configuration.html +## Source: https://github.com/hexojs/hexo/ + +# Site +title: Hexo +subtitle: '' +description: '' +keywords: +author: John Doe +language: zh +timezone: '' + +# URL +## If your site is put in a subdirectory, set url as 'http://yoursite.com/child' and root as '/child/' +url: https://renbaoshuo.github.io +root: /hexo-theme-pure/ +permalink: :year/:month/:day/:title/ +permalink_defaults: +pretty_urls: + trailing_index: true # Set to false to remove trailing 'index.html' from permalinks + trailing_html: true # Set to false to remove trailing '.html' from permalinks + +# Directory +source_dir: source +public_dir: public +tag_dir: tags +archive_dir: archives +category_dir: categories +code_dir: downloads/code +i18n_dir: :lang +skip_render: + +# Writing +new_post_name: :title.md # File name of new posts +default_layout: post +titlecase: false # Transform title into titlecase +external_link: + enable: true # Open external links in new tab + field: site # Apply to the whole site + exclude: '' +filename_case: 0 +render_drafts: false +post_asset_folder: false +relative_link: false +future: true +highlight: + enable: true + line_number: false + auto_detect: true + tab_replace: ' ' + wrap: false + hljs: true +prismjs: + enable: false + +# Home page setting +# path: Root path for your blogs index page. (default = '') +# per_page: Posts displayed per page. (0 = disable pagination) +# order_by: Posts order. (Order by date descending by default) +index_generator: + path: '' + per_page: 10 + order_by: -date + +# Category & Tag +default_category: uncategorized +category_map: +tag_map: + +# Metadata elements +## https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta +meta_generator: true + +# Date / Time format +## Hexo uses Moment.js to parse and display date +## You can customize the date format as defined in +## http://momentjs.com/docs/#/displaying/format/ +date_format: YYYY-MM-DD +time_format: HH:mm:ss +## updated_option supports 'mtime', 'date', 'empty' +updated_option: 'mtime' + +# Pagination +## Set per_page to 0 to disable pagination +per_page: 10 +pagination_dir: page + +# Include / Exclude file(s) +## include:/exclude: options only apply to the 'source/' folder +include: +exclude: +ignore: + +# Extensions +## Plugins: https://hexo.io/plugins/ +## Themes: https://hexo.io/themes/ +theme: pure + +# Deployment +## Docs: https://hexo.io/docs/one-command-deployment +deploy: + type: '' \ No newline at end of file diff --git a/themes/pure/_config.yml b/themes/pure/_config.yml new file mode 100644 index 0000000..56f875d --- /dev/null +++ b/themes/pure/_config.yml @@ -0,0 +1,58 @@ +favicon: data:image/png;base64,%0A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 +avatar: data:image/png;base64,%0A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 +katex: false +force_https: true +check_update: true + +menus: + - name: 首页 + link: / + - name: 归档 + link: /archives/ + - name: 标签 + link: /tags/ + - name: 时间线 + link: /timeline/ + - name: 关于 + link: /about/ + - name: 友链 + link: /friends/ + - name: rss + link: /atom.xml + +post: + feature: true +disqus: + enable: false + shortname: "Your shortname here" + api: "Your api here" + apikey: "Your api key here" + admin: "Your disqus username here" +gitalk: + enable: false + clientId: "387beb7791af325600a4" + clientSecret: "b2b890ffae6bd3526351c3ed79ba4f8ab9cbd4f6" + repository: "BlogComment" + owner: "hzgcoding" + admin: "hzgcoding" +valine: + enable: false + appId: "Your appID here" + appKey: "Your appKey here" + placeholder: "Your placeholder here" + pageSize: 10 +web_analytics: + enable: false + google: UA-12345678-1 + baidu: 12345678 +footer: | + Footer HTML +friends: + - name: 777 + link: http://younfor.com/ + logo: https://younfor.com/images/avatar.png + description: "字节跳动大神,算法大佬" + - name: hl174 + link: http://hl174.com/ + logo: https://younfor.com/images/avatar.png + description: "深圳腾讯大神,算法大佬" diff --git a/themes/pure/layout/_includes/disqus.ejs b/themes/pure/layout/_includes/disqus.ejs new file mode 100644 index 0000000..02c694d --- /dev/null +++ b/themes/pure/layout/_includes/disqus.ejs @@ -0,0 +1,16 @@ +<% if(theme.disqus && theme.disqus.enable) { %> +
+ + + +<% } %> diff --git a/themes/pure/layout/_includes/footer.ejs b/themes/pure/layout/_includes/footer.ejs new file mode 100644 index 0000000..6ebf597 --- /dev/null +++ b/themes/pure/layout/_includes/footer.ejs @@ -0,0 +1,10 @@ + diff --git a/themes/pure/layout/_includes/gitalk.ejs b/themes/pure/layout/_includes/gitalk.ejs new file mode 100644 index 0000000..fde9e19 --- /dev/null +++ b/themes/pure/layout/_includes/gitalk.ejs @@ -0,0 +1,21 @@ +<% if (theme.gitalk.enable) { %> + + + + +
+ + +<% } %> diff --git a/themes/pure/layout/_includes/head.ejs b/themes/pure/layout/_includes/head.ejs new file mode 100644 index 0000000..52a6155 --- /dev/null +++ b/themes/pure/layout/_includes/head.ejs @@ -0,0 +1,54 @@ + + + + +<%= page.archive ? `归档 - ${config.title}` : page.title ? `${page.title} - ${config.title}` : `${config.title}` %> + + + + + + + + +<% if(theme.katex) { %> + + + +<% } %> + + +<%- css('styles/main') %> + + +<% if (theme.web_analytics && theme.web_analytics.enable) { %> + <% if (theme.web_analytics.google) { %> + + + + <% } %> + <% if (theme.web_analytics.baidu) { %> + + + <% } %> +<% } %> + + +<% if (theme.force_https) { %> + +<% } %> diff --git a/themes/pure/layout/_includes/header.ejs b/themes/pure/layout/_includes/header.ejs new file mode 100644 index 0000000..5e64c83 --- /dev/null +++ b/themes/pure/layout/_includes/header.ejs @@ -0,0 +1,36 @@ + + + diff --git a/themes/pure/layout/_includes/valine.ejs b/themes/pure/layout/_includes/valine.ejs new file mode 100644 index 0000000..3b9da55 --- /dev/null +++ b/themes/pure/layout/_includes/valine.ejs @@ -0,0 +1,15 @@ +<% if (theme.valine.enable) { %> + + +
+ + +<% } %> \ No newline at end of file diff --git a/themes/pure/layout/about.ejs b/themes/pure/layout/about.ejs new file mode 100644 index 0000000..4b23565 --- /dev/null +++ b/themes/pure/layout/about.ejs @@ -0,0 +1,14 @@ +
+
+
+

关于

+
+ <%- page.content %> +
+
+
+
+ <%- partial("_includes/disqus") %> + <%- partial("_includes/gitalk") %> + <%- partial("_includes/valine") %> +
diff --git a/themes/pure/layout/archive.ejs b/themes/pure/layout/archive.ejs new file mode 100644 index 0000000..729f074 --- /dev/null +++ b/themes/pure/layout/archive.ejs @@ -0,0 +1,44 @@ +
+
+ <% + if (!page.year) { + var years = []; + page.posts.forEach((post) => { + const post_year = post.date.year(); + if(!years.includes(post_year)) { + years.push(post_year); + } + }); + } else { + var years = [ page.year ]; + } + %> + <% years.forEach((year) => { %> +

<%- year %>

+ <% if(page.month) { %> +

<%- page.month %> 月

+ <% } %> + <% page.posts.forEach((post) => { %> + <% if(post.date.year() == year) { %> +
+ +
+ <%= date(post.date) %> +
<%= post.title || '(无标题)' %>
+
+
+
+ <% } %> + <% }); %> + <% }); %> +
+
+ +
+ <% if (page.prev) { %> + 上一页 + <% } %> + <% if (page.next) { %> + 下一页 + <% } %> +
diff --git a/themes/pure/layout/friends.ejs b/themes/pure/layout/friends.ejs new file mode 100644 index 0000000..40c242b --- /dev/null +++ b/themes/pure/layout/friends.ejs @@ -0,0 +1,34 @@ +
+
+
+

友情链接

+
+
<%- page.content %>
+ + <% if (theme.friends) { %> + <% theme.friends.forEach(function(friend) { %> +
+ + + +
+ <%= friend.name %> +
+
+ <%= friend.description %> +
+
+
+ <% }); %> + <% } else { %> + 暂无友链,快来申请吧! + <% } %> +
+
+
+ <%- partial("_includes/disqus.ejs") %> + <%- partial("_includes/gitalk") %> + <%- partial("_includes/valine") %> +
diff --git a/themes/pure/layout/index.ejs b/themes/pure/layout/index.ejs new file mode 100644 index 0000000..8723ff2 --- /dev/null +++ b/themes/pure/layout/index.ejs @@ -0,0 +1,44 @@ +
+
+ <% page.posts.forEach(function(post) { %> +
+
+ <% if (post.top) { %> + 置顶 + <% } %> + + <%= post.title || "(无标题)" %> + +
+

<%= post.excerpt.replace(/(<(\/?)(\w+)[^>]*>)|()/g ,'').replace(/\s+/g,' ') %>

+
+ +
+ <% if (theme.post.feature && post.feature) { %> + + + <% } %> +
+ <% }); %> +
+
+ +
+ <% if (page.prev) { %> + 上一页 + <% } %> + <% if (page.next) { %> + 下一页 + <% } %> +
diff --git a/themes/pure/layout/layout.ejs b/themes/pure/layout/layout.ejs new file mode 100644 index 0000000..f8c59d9 --- /dev/null +++ b/themes/pure/layout/layout.ejs @@ -0,0 +1,15 @@ + + + + <%- partial("_includes/head") %> + + +
+
+ <%- partial("_includes/header") %> + <%- body %> + <%- partial("_includes/footer") %> +
+
+ + \ No newline at end of file diff --git a/themes/pure/layout/post.ejs b/themes/pure/layout/post.ejs new file mode 100644 index 0000000..42d5904 --- /dev/null +++ b/themes/pure/layout/post.ejs @@ -0,0 +1,45 @@ +
+
+
+

<%= page.title ? page.title : "(无标题)" %>

+ +
+
+ <%- page.content %> +
+
+
+
+ <% if (page.next || page.prev) { %> +
+ <% if (page.prev) { %> + + <% } %> + <% if (page.next) { %> + + <% } %> +
+ <% } %> + <%- partial("_includes/disqus.ejs") %> + <%- partial("_includes/gitalk") %> + <%- partial("_includes/valine") %> +
diff --git a/themes/pure/layout/tag.ejs b/themes/pure/layout/tag.ejs new file mode 100644 index 0000000..257c1df --- /dev/null +++ b/themes/pure/layout/tag.ejs @@ -0,0 +1,41 @@ +
+

含有 <%= page.tag %> 标签的文章

+
+
+
+ <% page.posts.forEach(function(post) { %> + <% if(post.published) { %> +
+
+ + <%= post.title %> + +
+

<%= post.excerpt.replace(/(<(\/?)(\w+)[^>]*>)|()/g ,'').replace(/\s+/g,' ') %>

+
+ +
+
+ <% } %> + <% }); %> +
+
+ +
+ <% if (page.prev) { %> + 上一页 + <% } %> + <% if (page.next) { %> + 下一页 + <% } %> +
diff --git a/themes/pure/layout/tags.ejs b/themes/pure/layout/tags.ejs new file mode 100644 index 0000000..a5fc96d --- /dev/null +++ b/themes/pure/layout/tags.ejs @@ -0,0 +1,6 @@ +
+ <% site.tags.forEach((tag) => { %> + #<%= tag.name %> + <% }); %> +
diff --git a/themes/pure/package-lock.json b/themes/pure/package-lock.json new file mode 100644 index 0000000..c1cb72f --- /dev/null +++ b/themes/pure/package-lock.json @@ -0,0 +1,416 @@ +{ + "name": "hexo-theme-pure", + "version": "1.0.2", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "version": "1.0.1", + "license": "GPL-3.0", + "dependencies": { + "hexo-renderer-ejs": "^1.0.0", + "hexo-renderer-less": "^2.0.2" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/copy-anything": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.3.tgz", + "integrity": "sha512-GK6QUtisv4fNS+XcI7shX0Gx9ORg7QqIznyfho79JTnX1XhLiyZHfftvGiziqzRiEi/Bjhgpi+D2o7HxJFPnDQ==", + "dependencies": { + "is-what": "^3.12.0" + } + }, + "node_modules/ejs": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.7.4.tgz", + "integrity": "sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA==", + "hasInstallScript": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "optional": true, + "dependencies": { + "prr": "~1.0.1" + }, + "bin": { + "errno": "cli.js" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", + "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", + "optional": true + }, + "node_modules/hexo-renderer-ejs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hexo-renderer-ejs/-/hexo-renderer-ejs-1.0.0.tgz", + "integrity": "sha512-O925i69FG4NYO62oWORcPhRZZX0sPx1SXGKUS5DaR/lzajyiXH5i2sqnkj0ya0rNLXIy/D7Xmt7WbFyuQx/kKQ==", + "dependencies": { + "ejs": "^2.6.1" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/hexo-renderer-less": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hexo-renderer-less/-/hexo-renderer-less-2.0.2.tgz", + "integrity": "sha512-ieBAtFm6fhWeJJzVPzCBcI94qXIrjokJ25eBg2xBsB6Tj1JGzsKqUt8dPKNIxgudYguf+HX5eViIbqGCeelS9g==", + "dependencies": { + "less": "^3.9.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=", + "optional": true, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-what": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", + "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==" + }, + "node_modules/less": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/less/-/less-3.13.1.tgz", + "integrity": "sha512-SwA1aQXGUvp+P5XdZslUOhhLnClSLIjWvJhmd+Vgib5BFIr9lMNlQwmwUNOjXThF/A0x+MCYYPeWEfeWiLRnTw==", + "dependencies": { + "copy-anything": "^2.0.1", + "tslib": "^1.10.0" + }, + "bin": { + "lessc": "bin/lessc" + }, + "engines": { + "node": ">=6" + }, + "optionalDependencies": { + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "make-dir": "^2.1.0", + "mime": "^1.4.1", + "native-request": "^1.0.5", + "source-map": "~0.6.0" + } + }, + "node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "optional": true, + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dependencies": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "optional": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/native-request": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/native-request/-/native-request-1.0.9.tgz", + "integrity": "sha512-KTRwqMwWCkoLZfjes3yBhK6XHwZ5Q1jPsdVra9hug8HNRbMsfTJm8a8L6/WOYi1h5eWNwlBaYy8V5SpJwkDgKw==", + "optional": true + }, + "node_modules/picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "optional": true + }, + "node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "optional": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + }, + "dependencies": { + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "requires": { + "fill-range": "^7.0.1" + } + }, + "copy-anything": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.3.tgz", + "integrity": "sha512-GK6QUtisv4fNS+XcI7shX0Gx9ORg7QqIznyfho79JTnX1XhLiyZHfftvGiziqzRiEi/Bjhgpi+D2o7HxJFPnDQ==", + "requires": { + "is-what": "^3.12.0" + } + }, + "ejs": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.7.4.tgz", + "integrity": "sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA==" + }, + "errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "optional": true, + "requires": { + "prr": "~1.0.1" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "graceful-fs": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", + "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", + "optional": true + }, + "hexo-renderer-ejs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hexo-renderer-ejs/-/hexo-renderer-ejs-1.0.0.tgz", + "integrity": "sha512-O925i69FG4NYO62oWORcPhRZZX0sPx1SXGKUS5DaR/lzajyiXH5i2sqnkj0ya0rNLXIy/D7Xmt7WbFyuQx/kKQ==", + "requires": { + "ejs": "^2.6.1" + } + }, + "hexo-renderer-less": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hexo-renderer-less/-/hexo-renderer-less-2.0.2.tgz", + "integrity": "sha512-ieBAtFm6fhWeJJzVPzCBcI94qXIrjokJ25eBg2xBsB6Tj1JGzsKqUt8dPKNIxgudYguf+HX5eViIbqGCeelS9g==", + "requires": { + "less": "^3.9.0", + "micromatch": "^4.0.2" + } + }, + "image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=", + "optional": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + }, + "is-what": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", + "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==" + }, + "less": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/less/-/less-3.13.1.tgz", + "integrity": "sha512-SwA1aQXGUvp+P5XdZslUOhhLnClSLIjWvJhmd+Vgib5BFIr9lMNlQwmwUNOjXThF/A0x+MCYYPeWEfeWiLRnTw==", + "requires": { + "copy-anything": "^2.0.1", + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "make-dir": "^2.1.0", + "mime": "^1.4.1", + "native-request": "^1.0.5", + "source-map": "~0.6.0", + "tslib": "^1.10.0" + } + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "optional": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "optional": true + }, + "native-request": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/native-request/-/native-request-1.0.9.tgz", + "integrity": "sha512-KTRwqMwWCkoLZfjes3yBhK6XHwZ5Q1jPsdVra9hug8HNRbMsfTJm8a8L6/WOYi1h5eWNwlBaYy8V5SpJwkDgKw==", + "optional": true + }, + "picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==" + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "optional": true + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "optional": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "optional": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "optional": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "requires": { + "is-number": "^7.0.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } +} diff --git a/themes/pure/package.json b/themes/pure/package.json new file mode 100644 index 0000000..5482a57 --- /dev/null +++ b/themes/pure/package.json @@ -0,0 +1,30 @@ +{ + "name": "hexo-theme-pure", + "version": "1.0.2", + "description": "A modern and simple theme for Hexo.", + "scripts": { + "build:styles": "./node_modules/.bin/gulp" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/renbaoshuo/hexo-theme-pure.git" + }, + "keywords": [ + "hexo", + "hexo-theme" + ], + "author": { + "name": "Ren Baoshuo", + "email": "i@baoshuo.ren", + "url": "https://baoshuo.ren" + }, + "license": "GPL-3.0", + "bugs": { + "url": "https://github.com/renbaoshuo/hexo-theme-pure/issues" + }, + "homepage": "https://github.com/renbaoshuo/hexo-theme-pure#readme", + "dependencies": { + "hexo-renderer-ejs": "^1.0.0", + "hexo-renderer-less": "^2.0.2" + } +} diff --git a/themes/pure/scripts/.gitkeep b/themes/pure/scripts/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/themes/pure/scripts/check-updates.js b/themes/pure/scripts/check-updates.js new file mode 100644 index 0000000..8e24658 --- /dev/null +++ b/themes/pure/scripts/check-updates.js @@ -0,0 +1,58 @@ +'use strict'; + +const https = require('https'); +const path = require('path'); +const { version } = require(path.normalize(path.join(hexo.theme_dir, 'package.json'))); + +hexo.extend.filter.register('before_exit', () => { + if (!hexo.theme.config.check_update) return; + + const errorLog = (error) => { + hexo.log.warn('无法获取最新版本信息,请访问 https://github.com/renbaoshuo/hexo-theme-pure 了解详情'); + hexo.log.debug(error); + }; + + https.get('https://api.github.com/repos/renbaoshuo/hexo-theme-pure/releases/latest', { + headers: { + 'User-Agent': `hexo-theme-pure/${version}` + } + }, (res) => { + let result = ''; + res.on('data', data => { + result += data; + }); + res.on('end', () => { + try { + const tag = JSON.parse(result).tag_name; + if (!tag) { + errorLog('Missing release tag'); + return; + } + const latest = tag.replace('v', '').split('.'); + const current = version.split('.'); + + let isOutdated = false; + for (let i = 0; i < Math.max(latest.length, current.length); i++) { + if (!current[i] || latest[i] > current[i]) { + isOutdated = true; + break; + } + if (latest[i] < current[i]) { + break; + } + } + + if (isOutdated) { + hexo.log.warn('检测到主题更新,请前往 https://github.com/renbaoshuo/hexo-theme-pure/releases 页面下载'); + hexo.log.warn(`当前版本为 v${current.join('.')},最新版本为 v${latest.join('.')}`); + } else { + hexo.log.info(`当前使用的是最新版本的主题(v${current.join('.')})`); + } + } catch (err) { + errorLog(err); + } + }); + }).on('error', err => { + errorLog(err); + }); +}); diff --git a/themes/pure/source/.nojekyll b/themes/pure/source/.nojekyll new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/themes/pure/source/.nojekyll @@ -0,0 +1 @@ + diff --git a/themes/pure/source/scripts/.gitkeep b/themes/pure/source/scripts/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/themes/pure/source/styles/_includes/about.less b/themes/pure/source/styles/_includes/about.less new file mode 100644 index 0000000..e69de29 diff --git a/themes/pure/source/styles/_includes/archive.less b/themes/pure/source/styles/_includes/archive.less new file mode 100644 index 0000000..1013289 --- /dev/null +++ b/themes/pure/source/styles/_includes/archive.less @@ -0,0 +1,48 @@ +// Archives +.archives-container { + padding: 32px; + display: flex; + flex-direction: column; + margin-left: 10%; + margin-right: 10%; + align-self: center; + max-width: 1000px; + min-width: 1000px; + + .year { + font-size: 34px; + font-weight: bold; + margin-top: 16px; + margin-bottom: 16px; + align-self: start; + } + + .post { + padding: 16px 0; + + .post-title { + font-size: 16px; + + small { + font-size: 80%; + } + + .archives-post-title { + display: inline-block; + margin-left: 20px; + } + } + } + + a:hover { + text-decoration: none !important; + } + + @media (max-width: 600px) { + padding: 16px !important; + } + + @media (max-width: 992px) { + min-width: 0px !important; + } +} diff --git a/themes/pure/source/styles/_includes/disqus.less b/themes/pure/source/styles/_includes/disqus.less new file mode 100644 index 0000000..e1bfc36 --- /dev/null +++ b/themes/pure/source/styles/_includes/disqus.less @@ -0,0 +1,22 @@ +// DisqusJS Styles + +#disqus_thread { + padding: 24px 32px; + width: 80%; + max-width: 1000px; +} + +@media (max-width: 992px) { + #disqus_thread { + padding: 24px 32px; + width: 100%; + } +} + +#disqus_thread ul li { + list-style: none; +} + +#dsqjs { + font-family: 'PingFang SC', -apple-system, BlinkMacSystemFont, opensans, Optima, 'Microsoft Yahei', sans-serif; +} diff --git a/themes/pure/source/styles/_includes/footer.less b/themes/pure/source/styles/_includes/footer.less new file mode 100644 index 0000000..64d8048 --- /dev/null +++ b/themes/pure/source/styles/_includes/footer.less @@ -0,0 +1,19 @@ +// Footer +.site-footer { + font-size: 12px; + padding: 24px; + max-width: 1000px; + min-width: 1000px; + align-self: center; + margin: 0 auto; + + a { + color: var(--content-first) !important; + } +} + +@media (max-width: 992px) { + .site-footer { + display: none; + } +} diff --git a/themes/pure/source/styles/_includes/friends.less b/themes/pure/source/styles/_includes/friends.less new file mode 100644 index 0000000..7ffb421 --- /dev/null +++ b/themes/pure/source/styles/_includes/friends.less @@ -0,0 +1,45 @@ +.friend-box { + float: left; + max-width: calc(50% - 20px); + min-width: calc(50% - 20px); + margin: 15px 10px; + background-color: rgba(20, 153, 196, 0.125); + padding: 15px; + border-radius: 15px; + + img.friend-avatar { + width: 70px; + height: 70px; + border-radius: 50% !important; + float: left; + margin: 0 15px 0 0 !important; + } + + .friend-info { + height: 70px; + overflow: hidden; + line-height: 24px; + padding-left: 30px; + font-size: 14px; + + a { + text-decoration: none !important; + font-size: 18px; + background: linear-gradient(180deg, transparent 75%, rgba(116, 192, 252, 0.4) 0); + margin-bottom: 10px; + } + + .friend-info-description { + margin-top: 10px; + + @media (max-width: 465px) { + display: none; + } + } + } + + @media (max-width: 993px) { + max-width: 90% !important; + min-width: 90% !important; + } +} diff --git a/themes/pure/source/styles/_includes/gitalk.less b/themes/pure/source/styles/_includes/gitalk.less new file mode 100644 index 0000000..f71b57a --- /dev/null +++ b/themes/pure/source/styles/_includes/gitalk.less @@ -0,0 +1,12 @@ +// Gitalk Styles + +#gitalk-container { + width: 80%; + max-width: 1000px; +} + +@media (max-width: 992px) { + #gitalk-container { + width: 100%; + } +} diff --git a/themes/pure/source/styles/_includes/header.less b/themes/pure/source/styles/_includes/header.less new file mode 100644 index 0000000..6810a2d --- /dev/null +++ b/themes/pure/source/styles/_includes/header.less @@ -0,0 +1,165 @@ +// Navbar +.navbar { + padding: 38px 96px !important; + + .navbar-brand { + display: flex; + flex-direction: row; + align-items: center; + + .user-avatar { + width: 40px; + height: 40px; + border-radius: 50%; + } + + .site-name { + font-size: 28px !important; + font-weight: bold; + margin-left: 16px; + } + } + + .navbar-nav { + width: 100%; + justify-content: flex-end; + + .nav-item { + padding: 14px 26px; + + a { + font-size: 15px; + flex-shrink: 0; + } + } + } +} + +@media (max-width: 992px) { + .navbar { + padding: 30px 30px 0 30px !important; + margin-bottom: 30px; + + .navbar-brand { + .user-avatar { + width: 31px; + height: 31px; + border-radius: 50%; + } + .site-name { + font-size: 21px !important; + font-weight: bold; + } + } + .nav-item { + padding: 16px 24px; + &:first-child { + margin-top: 35px; + } + } + } +} + +@media (min-width: 992px) { + .navbar { + max-width: 950px; + align-items: center !important; + align-self: center !important; + padding-left: 0px !important; + padding-right: 0px !important; + min-width: 950px; + margin: auto; + margin-top: 0px; + margin-bottom: 0px; + } +} + +// navbar style form bootstrap v4.5 +.navbar { + position: relative; + display: -ms-flexbox; + display: flex; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + -ms-flex-align: center; + align-items: center; + -ms-flex-pack: justify; + justify-content: space-between; + padding: 0.5rem 1rem; +} + +.navbar-brand { + display: inline-block; + padding-top: 0.3125rem; + padding-bottom: 0.3125rem; + margin-right: 1rem; + font-size: 1.25rem; + line-height: inherit; + white-space: nowrap; + + &:hover, + &:focus { + text-decoration: none; + } +} + +.navbar-nav { + display: -ms-flexbox; + display: flex; + -ms-flex-direction: column; + flex-direction: column; + padding-left: 0; + margin-bottom: 0; + list-style: none; +} + +.navbar-collapse { + -ms-flex-preferred-size: 100%; + flex-basis: 100%; + -ms-flex-positive: 1; + flex-grow: 1; + -ms-flex-align: center; + align-items: center; +} + +.navbar-toggler { + padding: 0.25rem 0.75rem; + font-size: 1.25rem; + line-height: 1; + background-color: transparent; + border: 1px solid transparent; + border-radius: 0.25rem; + + &:hover, + &:focus { + text-decoration: none; + } +} + +@media (min-width: 992px) { + .navbar-expand-lg { + -ms-flex-flow: row nowrap; + flex-flow: row nowrap; + -ms-flex-pack: start; + justify-content: flex-start; + .navbar-nav { + -ms-flex-direction: row; + flex-direction: row; + } + .navbar-collapse { + display: -ms-flexbox !important; + display: flex !important; + -ms-flex-preferred-size: auto; + flex-basis: auto; + } + .navbar-toggler { + display: none; + } + } +} + +@media (max-width: 991px) { + #navbarSupportedContent { + display: none; + } +} diff --git a/themes/pure/source/styles/_includes/highlight.less b/themes/pure/source/styles/_includes/highlight.less new file mode 100644 index 0000000..573f40d --- /dev/null +++ b/themes/pure/source/styles/_includes/highlight.less @@ -0,0 +1,91 @@ +.hljs { + display: block; + overflow-x: auto; + padding: 0.5em; + color: #383a42; + background: #eeeeee; + + .hljs-comment, + .hljs-quote { + color: #a0a1a7; + font-style: italic; + } + + .hljs-doctag, + .hljs-formula, + .hljs-keyword { + color: #a626a4; + } + + .hljs-deletion, + .hljs-name, + .hljs-section, + .hljs-selector-tag, + .hljs-subst { + color: #e45649; + } + + .hljs-literal { + color: #0184bb; + } + + .hljs-addition, + .hljs-attribute, + .hljs-meta-string, + .hljs-regexp, + .hljs-string { + color: #50a14f; + } + + .hljs-built_in, + .hljs-class .title { + color: #c18401; + } + + .hljs-attr, + .hljs-number, + .hljs-selector-attr, + .hljs-selector-class, + .hljs-selector-pseudo, + .hljs-template-variable, + .hljs-type, + .hljs-variable { + color: #986801; + } + + .hljs-bullet, + .hljs-link, + .hljs-meta, + .hljs-selector-id, + .hljs-symbol, + .hljs-title { + color: #4078f2; + } + + .hljs-emphasis { + font-style: italic; + } + + .hljs-strong { + font-weight: 700; + } + + .hljs-link { + text-decoration: underline; + } + + &::-webkit-scrollbar { + width: 8px; + height: 6px; + } + + &::-webkit-scrollbar-thumb { + border-radius: 4px; + background-color: #cbcbcb !important; + } + + &::-webkit-scrollbar-track-piece, + &::-webkit-scrollbar-corner { + background: #eeeeee !important; + } +} diff --git a/themes/pure/source/styles/_includes/pagination.less b/themes/pure/source/styles/_includes/pagination.less new file mode 100644 index 0000000..edebcb2 --- /dev/null +++ b/themes/pure/source/styles/_includes/pagination.less @@ -0,0 +1,14 @@ +.pagination-container { + padding: 24px 32px 32px; + align-self: center; + + .prev-page { + margin: 0 16px; + font-size: 14px; + } + + .next-page { + margin: 0 16px; + font-size: 14px; + } +} diff --git a/themes/pure/source/styles/_includes/post-list.less b/themes/pure/source/styles/_includes/post-list.less new file mode 100644 index 0000000..01a66d3 --- /dev/null +++ b/themes/pure/source/styles/_includes/post-list.less @@ -0,0 +1,133 @@ +// Post list +.post-list-container { + display: flex; + flex-direction: column; + align-items: center; + + .post-inner { + min-width: 1000px; + max-width: 1000px; + + @media (max-width: 992px) { + min-width: 0; + width: 100%; + margin-top: 16px; + + .post { + margin: 12px 16px 12px 16px; + padding: 18px 22px 18px 22px; + + .post-left { + .post-title { + font-size: 17px; + font-weight: bold; + } + + .post-abstract { + margin-top: 10px; + } + + .post-info { + margin-top: 10px; + } + } + + .post-feature-image { + width: 0; + height: 0; + flex-basis: 0; + flex-shrink: 0; + margin-left: 0; + border-radius: 2px; + overflow: hidden; + background-size: cover; + background-position: center; + } + } + } + + .post { + margin: 30px; + border-radius: 4px; + padding: 28px; + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + transition: all 0.3s; + + .post-left { + width: 100%; + display: flex; + flex-direction: column; + + code { + color: #bfbfbf; + font-family: Consolas; + background: none !important; + padding: 0; + margin: 0; + } + + .sticky-top-flag { + color: white; + padding: 3px 10px; + font-size: 12px; + border-radius: 3px; + margin-right: 10px; + vertical-align: middle; + } + + .post-title { + font-size: 22px; + font-weight: normal; + vertical-align: middle; + } + + .post-abstract { + width: 100%; + margin-top: 20px; + line-height: 1.5; + } + + .post-abstract * { + font-size: 13px; + margin-bottom: 0; + } + + .post-info { + margin-top: 20px; + font-size: 12px; + display: flex; + justify-content: space-between; + + .post-tag, + .post-category { + margin: 0 10px; + color: var(--accent-first); + } + + a { + color: var(--accent-first); + } + } + } + + &:hover { + transform: scale(1.012); + } + + .post-feature-image { + width: 200px; + height: 100px; + flex-basis: 200px; + flex-shrink: 0; + margin-left: 20px; + border-radius: 2px; + overflow: hidden; + background-size: cover; + background-position: center; + } + } + } +} diff --git a/themes/pure/source/styles/_includes/post.less b/themes/pure/source/styles/_includes/post.less new file mode 100644 index 0000000..65c060a --- /dev/null +++ b/themes/pure/source/styles/_includes/post.less @@ -0,0 +1,308 @@ +// Post container +.post-container { + display: flex; + flex-direction: column; + align-items: center; + min-height: 100%; + flex: 1; + + .post-detail { + flex: 1; + padding: 24px 32px; + width: 1000px; + border-radius: 5px; + + .post-title { + font-size: 26px; + text-align: center; + padding: 24px; + font-weight: normal; + } + + .post-info { + text-align: center; + font-size: 12px; + padding-bottom: 24px; + + .post-tag { + padding: 8px; + } + } + + .post-toc { + margin-bottom: 30px; + + h2 { + display: inline-block; + background: linear-gradient(180deg, transparent 75%, rgba(139, 149, 159, 0.4) 0) !important; + } + + ul { + margin: 0px; + } + + .markdownIt-TOC { + padding: 0px; + + li { + list-style: none; + margin: 0px; + + ul li { + list-style-type: disc; + margin: 0px; + + a { + color: var(--content-first) !important; + } + } + } + } + } + + .post-content { + margin-top: 30px; + + h1, + h2, + h3, + h4, + h5, + h6 { + display: table !important; + } + + h2, + h3 { + display: inline-block; + color: var(--content-first) !important; + position: relative !important; + background: linear-gradient(180deg, transparent 75%, var(--accent-second) 0) !important; + } + } + + @media (max-width: 992px) { + width: 100%; + margin-top: 20px; + + .post-title { + font-size: 20px; + font-weight: bold; + } + } + } + + details { + background: var(--accent-second); + border-radius: 5px; + padding: 10px 20px; + + summary { + user-select: none; + font-size: 14px; + font-weight: bold; + padding: 10px 20px; + border-radius: 5px; + background: var(--accent-second); + margin: -10px -20px; + } + + &[open] summary { + border-radius: 5px 5px 0px 0px; + margin: -10px -20px 15px -20px; + } + + p { + font-size: 14px; + } + } +} + +.gt-a-link { + color: var(--content-first) !important; + position: relative !important; + background: linear-gradient(180deg, transparent 75%, #4dabf766 0) !important; +} + +.gt-post-content { + word-break: normal; + word-wrap: break-word; + + a { + &, + & code { + color: var(--accent-first) !important; + transition: all 0.3s; + } + + &:hover { + text-decoration: underline !important; + } + } + + .post-toc + hr { + margin-bottom: calc(1rem + 18px); + } + + img { + display: block; + max-width: 100%; + border-radius: 3px; + margin: 18px auto; + } + + p { + line-height: 1.725; + margin-bottom: 18px; + font-size: 16px; + letter-spacing: 1.0382px; + } + + pre { + margin-bottom: 18px; + } + + blockquote { + padding: 16px; + border: 0; + border-left: 4px; + border-style: solid; + margin-bottom: 16px; + background: var(--accent-second) !important; + border-color: var(--accent-first) !important; + + p { + margin-bottom: 0; + } + } + + table { + border-collapse: collapse; + margin: 1rem 0; + overflow-x: auto; + display: table; + width: 100%; + + thead th { + text-align: left; + } + + tr { + border-top: 1px solid var(--border); + } + + td, + th { + border: 1px solid var(--border); + padding: 0.6em 1em; + } + } + + ul, + ol { + padding-left: 20px; + line-height: 1.725; + margin-bottom: 16px; + } + + h1, + h2, + h3, + h4, + h5, + h6 { + margin-bottom: 18px; + padding-top: 14px; + font-weight: bold; + } + + h1 { + font-size: 28px; + } + + h2 { + font-size: 24px; + } + + h3 { + font-size: 20px; + } + + h4 { + font-size: 18px; + } + + h5 { + font-size: 16px; + } + + h6 { + font-size: 14px; + } + + pre { + margin-bottom: 18px; + margin-right: 32px; + + & > code { + letter-spacing: 0px; + font-size: 14px; + font-family: 'Roboto Mono', Consolas, Menlo, Monaco, 'Source Code Pro', 'Courier New', monospace; + padding: 1em; + border-radius: 5px; + line-height: 1.375; + position: relative; + width: 100%; + overflow: scroll; + display: block; + } + } + + p code, + li code, + table code { + letter-spacing: 0px; + color: var(--content-first); + font-family: 'Roboto Mono', Consolas, Menlo, Monaco, 'Source Code Pro', 'Courier New', monospace; + padding: 0.2em 0.4em; + margin: 0; + font-size: 85%; + background-color: #1b1f230d; + border-radius: 6px; + word-break: break-all; + } +} + +.post-detail { + margin-top: 0px !important; + padding-top: 0px !important; +} + +.next-prev-post { + text-align: center; + padding: 24px 32px; + + .next-post { + .next { + margin-bottom: 24px; + font-size: 14px; + } + + .post-title { + font-size: 16px; + font-weight: bold; + } + } + + .prev-post { + .prev { + margin-bottom: 24px; + font-size: 14px; + } + + .post-title { + font-size: 16px; + font-weight: bold; + } + } +} diff --git a/themes/pure/source/styles/_includes/tag.less b/themes/pure/source/styles/_includes/tag.less new file mode 100644 index 0000000..8ee4325 --- /dev/null +++ b/themes/pure/source/styles/_includes/tag.less @@ -0,0 +1,9 @@ +.current-tag-container .title { + text-align: center; + font-size: 18px; + margin-bottom: 24px; + + @media (max-width: 992px) { + margin-top: 50px; + } +} diff --git a/themes/pure/source/styles/_includes/tags.less b/themes/pure/source/styles/_includes/tags.less new file mode 100644 index 0000000..9b1d87f --- /dev/null +++ b/themes/pure/source/styles/_includes/tags.less @@ -0,0 +1,16 @@ +.tags-container { + padding: 32px 32px; + flex: 1; + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + flex-wrap: wrap; + max-width: 1000px; + align-self: center; + + .tag { + font-size: 15px; + margin: 5px 15px; + } +} diff --git a/themes/pure/source/styles/_includes/valine.less b/themes/pure/source/styles/_includes/valine.less new file mode 100644 index 0000000..2e3fbe0 --- /dev/null +++ b/themes/pure/source/styles/_includes/valine.less @@ -0,0 +1,12 @@ +// Valine Styles + +#vcomments { + width: 80%; + max-width: 1000px; +} + +@media (max-width: 992px) { + #vcomments { + width: 100%; + } +} diff --git a/themes/pure/source/styles/main.less b/themes/pure/source/styles/main.less new file mode 100644 index 0000000..c6f1f69 --- /dev/null +++ b/themes/pure/source/styles/main.less @@ -0,0 +1,147 @@ +:root { + --theme-first: #ffffff; + --theme-second: #f8f9fa; + --content-first: #343a40; + --content-second: #adb5bd; + --content-code: #f9f2f4; + --border: #dfe2e5; + --accent-first: #bfbfbf; + --accent-second: #8b959f66; +} + +.gt-bg-theme-color-first { + background: var(--theme-first); +} +.gt-bg-theme-color-second { + background: var(--theme-second); +} +.gt-bg-content-color-first { + background: var(--content-first); +} +.gt-bg-content-color-second { + background: var(--content-second); +} +.gt-bg-accent-color-first { + background: var(--accent-first); +} +.gt-bg-accent-color-second { + background: var(--accent-second); +} +.gt-c-theme-color-first { + color: var(--theme-first); +} +.gt-c-theme-color-second { + color: var(--theme-second); +} +.gt-c-content-color-first { + color: var(--content-first); +} +.gt-c-content-color-second { + color: var(--content-second); +} +.gt-c-accent-color-first { + color: var(--accent-first); +} +.gt-c-accent-color-second { + color: var(--accent-second); +} + +* { + &, + &:before, + &:after { + margin: 0; + padding: 0; + } + &:focus { + outline-width: 0px; + } + &:focus-visible { + outline-width: 1px; + } +} + +html { + font-size: 58%; +} + +body { + font-family: 'Open Sans', 'PingFang SC', -apple-system, BlinkMacSystemFont, opensans, Optima, 'Microsoft Yahei', + sans-serif; + font-size: 16px; + letter-spacing: 1.0382px; +} + +button { + outline: none !important; +} + +a { + text-decoration: none !important; + transition: all 0.3s; +} + +body, +div, +a, +p, +ul, +li, +ol, +h1, +h2, +h3, +h4, +h5, +h6, +table, +tr, +td { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +ol li { + list-style: decimal; + font-size: 16px; +} + +ul li { + list-style: disc; + font-size: 16px; +} + +hr { + margin: 1rem 0px; + border: 0; + border-top: 1px solid rgba(0, 0, 0, 0.1); +} + +.main { + max-width: 100%; + min-height: 100vh; + margin: 0 auto; + .main-content { + flex: 1; + display: flex; + min-height: 100vh; + flex-direction: column; + justify-content: space-between; + } +} + +@import './_includes/header.less'; +@import './_includes/footer.less'; +@import './_includes/post-list.less'; +@import './_includes/pagination.less'; +@import './_includes/post.less'; +@import './_includes/archive.less'; +@import './_includes/tag.less'; +@import './_includes/tags.less'; +@import './_includes/friends.less'; +@import './_includes/about.less'; +@import './_includes/disqus.less'; +@import './_includes/gitalk.less'; +@import './_includes/valine.less'; +@import './_includes/highlight.less'; diff --git a/timeline/index.html b/timeline/index.html deleted file mode 100644 index c43f989..0000000 --- a/timeline/index.html +++ /dev/null @@ -1,137 +0,0 @@ - - - - - - - -胡说八道 - Genge 随笔 - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - -
-
-
-

胡说八道

- -
-
- -
-
-
-
- - - - - - -
- - - -
-
- - \ No newline at end of file diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..be3e9fa --- /dev/null +++ b/yarn.lock @@ -0,0 +1,2615 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@types/color-name@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" + integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== + +JSONStream@^1.0.7: + version "1.3.5" + resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" + integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== + dependencies: + jsonparse "^1.2.0" + through ">=2.2.7 <3" + +a-sync-waterfall@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/a-sync-waterfall/-/a-sync-waterfall-1.0.1.tgz#75b6b6aa72598b497a125e7a2770f14f4c8a1fa7" + integrity sha512-RYTOHHdWipFUliRFMCS4X2Yn2X8M87V/OpSqWzKKOGhzqyUxzyVmhHDH9sAvG+ZuQf/TAOFsLCpMw09I1ufUnA== + +abbrev@1, abbrev@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== + +accepts@~1.3.5: + version "1.3.7" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" + integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== + dependencies: + mime-types "~2.1.24" + negotiator "0.6.2" + +acorn@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.0.tgz#949d36f2c292535da602283586c2477c57eb2d6c" + integrity sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ== + +align-text@^0.1.1, align-text@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" + integrity sha1-DNkKVhCT810KmSVsIrcGlDP60Rc= + dependencies: + kind-of "^3.0.2" + longest "^1.0.1" + repeat-string "^1.5.2" + +amdefine@>=0.0.4: + version "1.0.1" + resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" + integrity sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU= + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= + +ansi-regex@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" + integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.1.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359" + integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA== + dependencies: + "@types/color-name" "^1.1.1" + color-convert "^2.0.1" + +anymatch@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" + integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== + dependencies: + micromatch "^3.1.4" + normalize-path "^2.1.1" + +anymatch@~3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" + integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +aproba@^1.0.3: + version "1.2.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" + integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== + +archy@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" + integrity sha1-+cjBN1fMHde8N5rHeyxipcKGjEA= + +are-we-there-yet@~1.1.2: + version "1.1.5" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" + integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w== + dependencies: + delegates "^1.0.0" + readable-stream "^2.0.6" + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +arr-diff@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= + +arr-flatten@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== + +arr-union@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= + +array-unique@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= + +asap@^2.0.3: + version "2.0.6" + resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= + +assign-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= + +async-each@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" + integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== + +async@~0.2.6: + version "0.2.10" + resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1" + integrity sha1-trvgsGdLnXGXCMo43owjfLUmw9E= + +atob@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" + integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + +base@^0.11.1: + version "0.11.2" + resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== + dependencies: + cache-base "^1.0.1" + class-utils "^0.3.5" + component-emitter "^1.2.1" + define-property "^1.0.0" + isobject "^3.0.1" + mixin-deep "^1.2.0" + pascalcase "^0.1.1" + +basic-auth@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.1.tgz#b998279bf47ce38344b4f3cf916d4679bbf51e3a" + integrity sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg== + dependencies: + safe-buffer "5.1.2" + +binary-extensions@^1.0.0: + version "1.13.1" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" + integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== + +binary-extensions@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.0.0.tgz#23c0df14f6a88077f5f986c0d167ec03c3d5537c" + integrity sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow== + +bindings@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" + integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== + dependencies: + file-uri-to-path "1.0.0" + +bluebird@^3.2.2, bluebird@^3.5.1, bluebird@^3.5.2, bluebird@^3.5.5: + version "3.7.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^2.3.1, braces@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" + integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== + dependencies: + arr-flatten "^1.1.0" + array-unique "^0.3.2" + extend-shallow "^2.0.1" + fill-range "^4.0.0" + isobject "^3.0.1" + repeat-element "^1.1.2" + snapdragon "^0.8.1" + snapdragon-node "^2.0.1" + split-string "^3.0.2" + to-regex "^3.0.1" + +braces@^3.0.1, braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +bytes@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" + integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg= + +cache-base@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== + dependencies: + collection-visit "^1.0.0" + component-emitter "^1.2.1" + get-value "^2.0.6" + has-value "^1.0.0" + isobject "^3.0.1" + set-value "^2.0.0" + to-object-path "^0.3.0" + union-value "^1.0.0" + unset-value "^1.0.0" + +camel-case@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73" + integrity sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M= + dependencies: + no-case "^2.2.0" + upper-case "^1.1.1" + +camel-case@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-4.1.1.tgz#1fc41c854f00e2f7d0139dfeba1542d6896fe547" + integrity sha512-7fa2WcG4fYFkclIvEmxBbTvmibwF2/agfEBc6q3lOpVu0A13ltLsA+Hr/8Hp6kp5f+G7hKi6t8lys6XxP+1K6Q== + dependencies: + pascal-case "^3.1.1" + tslib "^1.10.0" + +camelcase@^1.0.2: + version "1.2.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" + integrity sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk= + +camelcase@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" + integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8= + +center-align@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad" + integrity sha1-qg0yYptu6XIgBBHL1EYckHvCt60= + dependencies: + align-text "^0.1.3" + lazy-cache "^1.0.3" + +chalk@^2.4.1, chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" + integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chokidar@^2.0.0: + version "2.1.8" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" + integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg== + dependencies: + anymatch "^2.0.0" + async-each "^1.0.1" + braces "^2.3.2" + glob-parent "^3.1.0" + inherits "^2.0.3" + is-binary-path "^1.0.0" + is-glob "^4.0.0" + normalize-path "^3.0.0" + path-is-absolute "^1.0.0" + readdirp "^2.2.1" + upath "^1.1.1" + optionalDependencies: + fsevents "^1.2.7" + +chokidar@^3.0.0: + version "3.3.1" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.3.1.tgz#c84e5b3d18d9a4d77558fef466b1bf16bbeb3450" + integrity sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.3.0" + optionalDependencies: + fsevents "~2.1.2" + +chownr@^1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.3.tgz#42d837d5239688d55f303003a508230fa6727142" + integrity sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw== + +class-utils@^0.3.5: + version "0.3.6" + resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== + dependencies: + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.1" + +cliui@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1" + integrity sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE= + dependencies: + center-align "^0.1.1" + right-align "^0.1.1" + wordwrap "0.0.2" + +cliui@^3.0.3: + version "3.2.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" + integrity sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0= + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + wrap-ansi "^2.0.0" + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= + +collection-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= + dependencies: + map-visit "^1.0.0" + object-visit "^1.0.0" + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +command-exists@^1.2.8: + version "1.2.8" + resolved "https://registry.yarnpkg.com/command-exists/-/command-exists-1.2.8.tgz#715acefdd1223b9c9b37110a149c6392c2852291" + integrity sha512-PM54PkseWbiiD/mMsbvW351/u+dafwTJ0ye2qB60G1aGQP9j3xK2gmMDc+R34L3nDtx4qMCitXT75mkbkGJDLw== + +component-emitter@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" + integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== + +compressible@~2.0.16: + version "2.0.18" + resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" + integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== + dependencies: + mime-db ">= 1.43.0 < 2" + +compression@^1.7.4: + version "1.7.4" + resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" + integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== + dependencies: + accepts "~1.3.5" + bytes "3.0.0" + compressible "~2.0.16" + debug "2.6.9" + on-headers "~1.0.2" + safe-buffer "5.1.2" + vary "~1.1.2" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +connect@^3.7.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/connect/-/connect-3.7.0.tgz#5d49348910caa5e07a01800b030d0c35f20484f8" + integrity sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ== + dependencies: + debug "2.6.9" + finalhandler "1.1.2" + parseurl "~1.3.3" + utils-merge "1.0.1" + +console-control-strings@^1.0.0, console-control-strings@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= + +copy-descriptor@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= + +core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + +cross-spawn@^6.0.5: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + +cross-spawn@^7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.1.tgz#0ab56286e0f7c24e153d04cc2aa027e43a9a5d14" + integrity sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +css-parse@1.7.x: + version "1.7.0" + resolved "https://registry.yarnpkg.com/css-parse/-/css-parse-1.7.0.tgz#321f6cf73782a6ff751111390fc05e2c657d8c9b" + integrity sha1-Mh9s9zeCpv91ERE5D8BeLGV9jJs= + +css-parse@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/css-parse/-/css-parse-2.0.0.tgz#a468ee667c16d81ccf05c58c38d2a97c780dbfd4" + integrity sha1-pGjuZnwW2BzPBcWMONKpfHgNv9Q= + dependencies: + css "^2.0.0" + +css@^2.0.0: + version "2.2.4" + resolved "https://registry.yarnpkg.com/css/-/css-2.2.4.tgz#c646755c73971f2bba6a601e2cf2fd71b1298929" + integrity sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw== + dependencies: + inherits "^2.0.3" + source-map "^0.6.1" + source-map-resolve "^0.5.2" + urix "^0.1.0" + +cuid@^2.1.4: + version "2.1.8" + resolved "https://registry.yarnpkg.com/cuid/-/cuid-2.1.8.tgz#cbb88f954171e0d5747606c0139fb65c5101eac0" + integrity sha512-xiEMER6E7TlTPnDxrM4eRiC6TRgjNX9xzEZ5U/Se2YJKr7Mq4pJn/2XEHjl3STcSh96GmkHPcBXLES8M29wyyg== + +debug@*: + version "4.1.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" + integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== + dependencies: + ms "^2.1.1" + +debug@2.6.9, debug@^2.2.0, debug@^2.3.3: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@^3.2.6: + version "3.2.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" + integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== + dependencies: + ms "^2.1.1" + +debug@~3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== + dependencies: + ms "2.0.0" + +decamelize@^1.0.0, decamelize@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= + +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= + +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + +deepmerge@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" + integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== + +define-property@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= + dependencies: + is-descriptor "^0.1.0" + +define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= + dependencies: + is-descriptor "^1.0.0" + +define-property@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== + dependencies: + is-descriptor "^1.0.2" + isobject "^3.0.1" + +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= + +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= + +destroy@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= + +detect-libc@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= + +dom-serializer@^0.2.1: + version "0.2.2" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51" + integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g== + dependencies: + domelementtype "^2.0.1" + entities "^2.0.0" + +domelementtype@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.0.1.tgz#1f8bdfe91f5a78063274e803b4bdcedf6e94f94d" + integrity sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ== + +domhandler@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-3.0.0.tgz#51cd13efca31da95bbb0c5bee3a48300e333b3e9" + integrity sha512-eKLdI5v9m67kbXQbJSNn1zjh0SDzvzWVWtX+qEI3eMjZw8daH9k8rlj1FZY9memPwjiskQFbe7vHVVJIAqoEhw== + dependencies: + domelementtype "^2.0.1" + +domutils@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.0.0.tgz#15b8278e37bfa8468d157478c58c367718133c08" + integrity sha512-n5SelJ1axbO636c2yUtOGia/IcJtVtlhQbFiVDBZHKV5ReJO1ViX7sFEemtuyoAnBxk5meNSYgA8V4s0271efg== + dependencies: + dom-serializer "^0.2.1" + domelementtype "^2.0.1" + domhandler "^3.0.0" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= + +ejs@^2.6.1: + version "2.7.4" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.7.4.tgz#48661287573dcc53e366c7a1ae52c3a120eec9ba" + integrity sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA== + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= + +entities@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.0.tgz#68d6084cab1b079767540d80e56a39b423e4abf4" + integrity sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw== + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +escape-string-regexp@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" + integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= + +expand-brackets@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= + dependencies: + debug "^2.3.3" + define-property "^0.2.5" + extend-shallow "^2.0.1" + posix-character-classes "^0.1.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= + dependencies: + is-extendable "^0.1.0" + +extend-shallow@^3.0.0, extend-shallow@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= + dependencies: + assign-symbols "^1.0.0" + is-extendable "^1.0.1" + +extglob@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== + dependencies: + array-unique "^0.3.2" + define-property "^1.0.0" + expand-brackets "^2.1.4" + extend-shallow "^2.0.1" + fragment-cache "^0.2.1" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +file-uri-to-path@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== + +fill-range@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= + dependencies: + extend-shallow "^2.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range "^2.1.0" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +finalhandler@1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" + integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.3" + statuses "~1.5.0" + unpipe "~1.0.0" + +for-in@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= + +fragment-cache@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= + dependencies: + map-cache "^0.2.2" + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= + +fs-minipass@^1.2.5: + version "1.2.7" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" + integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== + dependencies: + minipass "^2.6.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +fsevents@^1.2.7: + version "1.2.11" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.11.tgz#67bf57f4758f02ede88fb2a1712fef4d15358be3" + integrity sha512-+ux3lx6peh0BpvY0JebGyZoiR4D+oYzdPZMKJwkZ+sFkNJzpL7tXc/wehS49gUAxg3tmMHPHZkA8JU2rhhgDHw== + dependencies: + bindings "^1.5.0" + nan "^2.12.1" + +fsevents@~2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.2.tgz#4c0a1fb34bc68e543b4b82a9ec392bfbda840805" + integrity sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA== + +gauge@~2.7.3: + version "2.7.4" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" + integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= + dependencies: + aproba "^1.0.3" + console-control-strings "^1.0.0" + has-unicode "^2.0.0" + object-assign "^4.1.0" + signal-exit "^3.0.0" + string-width "^1.0.1" + strip-ansi "^3.0.1" + wide-align "^1.1.0" + +get-value@^2.0.3, get-value@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= + +glob-parent@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" + integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4= + dependencies: + is-glob "^3.1.0" + path-dirname "^1.0.0" + +glob-parent@~5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.0.tgz#5f4c1d1e748d30cd73ad2944b3577a81b081e8c2" + integrity sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw== + dependencies: + is-glob "^4.0.1" + +glob@7.0.x: + version "7.0.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.0.6.tgz#211bafaf49e525b8cd93260d14ab136152b3f57a" + integrity sha1-IRuvr0nlJbjNkyYNFKsTYVKz9Xo= + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.2" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^6.0.1: + version "6.0.4" + resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22" + integrity sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI= + dependencies: + inflight "^1.0.4" + inherits "2" + minimatch "2 || 3" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.1.3: + version "7.1.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +graceful-fs@^4.1.11, graceful-fs@^4.1.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423" + integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-unicode@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= + +has-value@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= + dependencies: + get-value "^2.0.3" + has-values "^0.1.4" + isobject "^2.0.0" + +has-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= + dependencies: + get-value "^2.0.6" + has-values "^1.0.0" + isobject "^3.0.0" + +has-values@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= + +has-values@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + +hexo-bunyan@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/hexo-bunyan/-/hexo-bunyan-2.0.0.tgz#020d7a51104cc7fe50df9005020c359ac504309e" + integrity sha512-5XHYu/yJOgPFTC0AaEgFtPPaBJU4jC7R10tITJwTRJk7K93rgSpRV8jF3e0PPlPwXd4FphTawjljH5R8LjmtpQ== + optionalDependencies: + moment "^2.10.6" + mv "~2" + safe-json-stringify "~1" + +hexo-cli@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/hexo-cli/-/hexo-cli-3.1.0.tgz#a7414d4647333fe73762f86d77e5660659c0d05b" + integrity sha512-Rc2gX2DlsALaFBbfk1XYx2XmeVAX+C7Dxc7UwETZOcu3cbGsf2DpwYTfKQumW3jagi1icA4KgW9aSRPPZZj/zg== + dependencies: + abbrev "^1.1.1" + acorn "^7.0.0" + bluebird "^3.5.5" + chalk "^2.4.2" + command-exists "^1.2.8" + hexo-fs "^2.0.0" + hexo-log "^1.0.0" + hexo-util "^1.4.0" + minimist "^1.2.0" + resolve "^1.11.0" + tildify "^2.0.0" + +hexo-front-matter@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/hexo-front-matter/-/hexo-front-matter-1.0.0.tgz#0b48b1c1ed143e8cb25b3b223a9037385d910655" + integrity sha512-Hn8IIzgWWnxYTekrjnA0rxwWMoQHifyrxKMqVibmFaRKf4AQ2V6Xo13Jiso6CDwYfS+OdA41QS5DG1Y+QXA5gw== + dependencies: + js-yaml "^3.13.1" + +hexo-fs@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/hexo-fs/-/hexo-fs-2.0.0.tgz#0bc421bc0c975a85a25061aca9537fb59292d279" + integrity sha512-mtwjfh5IZMXVCoITtoV+LfWbrD7xCWyv8OTIrOmwUW4JR+7EEvuwqu+QDztt4RS0azxUuc1sKVK68Mxfp2AoYQ== + dependencies: + bluebird "^3.5.1" + chokidar "^3.0.0" + escape-string-regexp "^2.0.0" + graceful-fs "^4.1.11" + +hexo-generator-archive@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/hexo-generator-archive/-/hexo-generator-archive-1.0.0.tgz#ad2afb12232a65e2f8608fc1ca3f19162fb63786" + integrity sha512-24TeanDGpMBUIq37DHpSESQbeN6ssZ06edsGSI76tN4Yit50TgsgzP5g5DSu0yJk0jUtHJntysWE8NYAlFXibA== + dependencies: + hexo-pagination "1.0.0" + +hexo-generator-category@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/hexo-generator-category/-/hexo-generator-category-1.0.0.tgz#cd2a2b03eb326da3ef821d6e29408488cc132eb5" + integrity sha512-kmtwT1SHYL2ismbGnYQXNtqLFSeTdtHNbJIqno3LKROpCK8ybST5QVXF1bZI9LkFcXV/H8ilt8gfg4/dNNcQQQ== + dependencies: + hexo-pagination "1.0.0" + +hexo-generator-index@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/hexo-generator-index/-/hexo-generator-index-1.0.0.tgz#8c9f157dcfa0af4f8417f77a4eb5b3831f69a719" + integrity sha512-L25MdZ7e5ar/F8lIW+zBNNlA4f5A8CBUOYi1IQZCgL3wPVW+AWn66RSM5UVBAbiw5yxDeTHdk0sJYXbhSBaOFQ== + dependencies: + hexo-pagination "1.0.0" + +hexo-generator-tag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/hexo-generator-tag/-/hexo-generator-tag-1.0.0.tgz#54ec23de9409c75584ea81e36057a59031b022f1" + integrity sha512-JDoB2T1EncRlyGSjuAhkGxRfKkN8tq0i8tFlk9I4q2L6iYxPaUnFenhji0oxufTADC16/IchuPjmMk//dt8Msg== + dependencies: + hexo-pagination "1.0.0" + +hexo-i18n@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/hexo-i18n/-/hexo-i18n-1.0.0.tgz#7983fb3a313e90615b84dd8fa946a71c489ef5bd" + integrity sha512-yw90JHr7ybUHN/QOkpHmlWJj1luVk5/v8CUU5NRA0n4TFp6av8NT7ujZ10GDawgnQEdMHnN5PUfAbNIVGR6axg== + dependencies: + sprintf-js "^1.0.3" + +hexo-log@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/hexo-log/-/hexo-log-1.0.0.tgz#6f0d39e0e9e76b04a4617b242c3e101d5c4283c7" + integrity sha512-XlPzRtnsdrUfTSkLJPACQgWByybB56E79H8xIjGWj0GL+J/VqENsgc+GER0ytFwrP/6YKCerXdaUWOYMcv6aiA== + dependencies: + chalk "^2.4.1" + hexo-bunyan "^2.0.0" + +hexo-pagination@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/hexo-pagination/-/hexo-pagination-1.0.0.tgz#c9c0ca3665267b9e9d0a89fc3edcaf3276907dc1" + integrity sha512-miEVFgxchPr2qNWxw0JWpJ9R/Yaf7HjHBZVjvCCcqfbsLyYtCvIfJDxcEwz1sDOC/fLzYPqNnhUI73uNxBHRSA== + +hexo-renderer-ejs@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/hexo-renderer-ejs/-/hexo-renderer-ejs-1.0.0.tgz#43c4de49eeae224036a457df860eb1c14c68b4b4" + integrity sha512-O925i69FG4NYO62oWORcPhRZZX0sPx1SXGKUS5DaR/lzajyiXH5i2sqnkj0ya0rNLXIy/D7Xmt7WbFyuQx/kKQ== + dependencies: + ejs "^2.6.1" + +hexo-renderer-marked@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/hexo-renderer-marked/-/hexo-renderer-marked-2.0.0.tgz#458540b014141e7cf72145a82ec591be359cdba1" + integrity sha512-+LMjgPkJSUAOlWYHJnBXxUHwGqemGNlK/I+JNO4zA5rEHWNWZ9wNAZKd5g0lEVdMAZzAV54gCylXGURgMO4IAw== + dependencies: + hexo-util "1.0.0" + marked "^0.7.0" + strip-indent "^3.0.0" + +hexo-renderer-stylus@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/hexo-renderer-stylus/-/hexo-renderer-stylus-1.1.0.tgz#7a0a107bcf4b74fdf88f28da2db47b8dc9e208e5" + integrity sha512-aXfMuro2aQOvpM5pyPEModAPvqYi73VN4t37vGMQCbT0QTmw8YohEmUpO/G/1k6j88ong6344v+A0xrpUGQRnQ== + dependencies: + nib "^1.1.2" + stylus "^0.54.5" + +hexo-server@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/hexo-server/-/hexo-server-1.0.0.tgz#af89fcc0a07740ad2a7f33d1d003889aab22927e" + integrity sha512-eSY+a5oiGCG/3T6FrdrNRBkttMLJkM+oitY6ZMFowjcBiG2VNEhQmfWUDOykfvApZs4wPYBb//uXD/58tfe3mA== + dependencies: + bluebird "^3.5.5" + chalk "^2.4.2" + compression "^1.7.4" + connect "^3.7.0" + mime "^2.4.3" + morgan "^1.9.1" + open "^6.3.0" + serve-static "^1.14.1" + +hexo-util@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/hexo-util/-/hexo-util-1.0.0.tgz#2545efcd66e345ef92fe48a73a35470cd65f0b2a" + integrity sha512-oV1/Y7ablc7e3d2kFFvQ/Ypi/BfL/uDSc1oNaMcxqr/UOH8F0QkHZ0Dmv+yLrEpFNYrrhBA0uavo3e+EqHNjnQ== + dependencies: + bluebird "^3.5.2" + camel-case "^3.0.0" + cross-spawn "^6.0.5" + highlight.js "^9.13.1" + html-entities "^1.2.1" + striptags "^3.1.1" + +hexo-util@^1.4.0, hexo-util@^1.8.0: + version "1.8.1" + resolved "https://registry.yarnpkg.com/hexo-util/-/hexo-util-1.8.1.tgz#d8287561709e246a6a73f07fccaed2a82865a736" + integrity sha512-gzZmcdpYjG168xunWaxFAtNhBA7il6jeaSLxL2Mj7+XRg86RqZGCgHK00gI35aQvaUPKO3XLmWQl8QyN61fa5w== + dependencies: + bluebird "^3.5.2" + camel-case "^4.0.0" + cross-spawn "^7.0.0" + deepmerge "^4.2.2" + highlight.js "^9.13.1" + htmlparser2 "^4.0.0" + punycode.js "^2.1.0" + striptags "^3.1.1" + +hexo@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/hexo/-/hexo-4.2.0.tgz#dd306abe801ca47623cae7c86818af4425b466ec" + integrity sha512-j2Kzgx/eWF0IgSfUEsAAhr0mDaaqR3E1MTus0/Vrs+JpQdMiSlqSbudPf2S9O4FHi5CAzDCuzToGlg2kgoQxcA== + dependencies: + abbrev "^1.1.1" + archy "^1.0.0" + bluebird "^3.5.2" + chalk "^3.0.0" + hexo-cli "^3.0.0" + hexo-front-matter "^1.0.0" + hexo-fs "^2.0.0" + hexo-i18n "^1.0.0" + hexo-log "^1.0.0" + hexo-util "^1.8.0" + js-yaml "^3.12.0" + lodash "^4.17.11" + micromatch "^4.0.2" + moment "^2.22.2" + moment-timezone "^0.5.21" + nunjucks "^3.1.3" + pretty-hrtime "^1.0.3" + resolve "^1.8.1" + strip-ansi "^6.0.0" + strip-indent "^3.0.0" + swig-extras "0.0.1" + swig-templates "^2.0.3" + text-table "^0.2.0" + tildify "^2.0.0" + titlecase "^1.1.2" + warehouse "^3.0.1" + +highlight.js@^9.13.1: + version "9.18.0" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.18.0.tgz#6b1763cfcd53744313bd3f31f1210f7beb962c79" + integrity sha512-A97kI1KAUzKoAiEoaGcf2O9YPS8nbDTCRFokaaeBhnqjQTvbAuAJrQMm21zw8s8xzaMtCQBtgbyGXLGxdxQyqQ== + +html-entities@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.1.tgz#0df29351f0721163515dfb9e5543e5f6eed5162f" + integrity sha1-DfKTUfByEWNRXfueVUPl9u7VFi8= + +htmlparser2@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-4.0.0.tgz#6034658db65b7713a572a9ebf79f650832dceec8" + integrity sha512-cChwXn5Vam57fyXajDtPXL1wTYc8JtLbr2TN76FYu05itVVVealxLowe2B3IEznJG4p9HAYn/0tJaRlGuEglFQ== + dependencies: + domelementtype "^2.0.1" + domhandler "^3.0.0" + domutils "^2.0.0" + entities "^2.0.0" + +http-errors@~1.7.2: + version "1.7.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" + integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +iconv-lite@^0.4.4: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +ignore-walk@^3.0.1: + version "3.0.3" + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.3.tgz#017e2447184bfeade7c238e4aefdd1e8f95b1e37" + integrity sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw== + dependencies: + minimatch "^3.0.4" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +ini@~1.3.0: + version "1.3.5" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" + integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== + +invert-kv@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" + integrity sha1-EEqOSqym09jNFXqO+L+rLXo//bY= + +is-accessor-descriptor@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= + dependencies: + kind-of "^3.0.2" + +is-accessor-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" + integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== + dependencies: + kind-of "^6.0.0" + +is-binary-path@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" + integrity sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg= + dependencies: + binary-extensions "^1.0.0" + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-buffer@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== + +is-data-descriptor@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= + dependencies: + kind-of "^3.0.2" + +is-data-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" + integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== + dependencies: + kind-of "^6.0.0" + +is-descriptor@^0.1.0: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== + dependencies: + is-accessor-descriptor "^0.1.6" + is-data-descriptor "^0.1.4" + kind-of "^5.0.0" + +is-descriptor@^1.0.0, is-descriptor@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" + integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== + dependencies: + is-accessor-descriptor "^1.0.0" + is-data-descriptor "^1.0.0" + kind-of "^6.0.2" + +is-extendable@^0.1.0, is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= + +is-extendable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== + dependencies: + is-plain-object "^2.0.4" + +is-extglob@^2.1.0, is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + +is-glob@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" + integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo= + dependencies: + is-extglob "^2.1.0" + +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" + integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== + dependencies: + is-extglob "^2.1.1" + +is-number@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= + dependencies: + kind-of "^3.0.2" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-plain-object@^2.0.3, is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +is-plain-object@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-3.0.0.tgz#47bfc5da1b5d50d64110806c199359482e75a928" + integrity sha512-tZIpofR+P05k8Aocp7UI/2UTa9lTJSebCXpFFoR9aibpokDj/uXBsJ8luUu0tTVYKkMU6URDUuOfJZ7koewXvg== + dependencies: + isobject "^4.0.0" + +is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== + +is-wsl@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" + integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= + +isarray@1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= + dependencies: + isarray "1.0.0" + +isobject@^3.0.0, isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= + +isobject@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-4.0.0.tgz#3f1c9155e73b192022a80819bacd0343711697b0" + integrity sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA== + +js-yaml@^3.12.0, js-yaml@^3.13.1: + version "3.13.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" + integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +jsonparse@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" + integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA= + +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= + dependencies: + is-buffer "^1.1.5" + +kind-of@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= + dependencies: + is-buffer "^1.1.5" + +kind-of@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== + +kind-of@^6.0.0, kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +lazy-cache@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" + integrity sha1-odePw6UEdMuAhF07O24dpJpEbo4= + +lcid@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" + integrity sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU= + dependencies: + invert-kv "^1.0.0" + +lodash@^4.17.11: + version "4.17.15" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" + integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== + +longest@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" + integrity sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc= + +lower-case@^1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac" + integrity sha1-miyr0bno4K6ZOkv31YdcOcQujqw= + +lower-case@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.1.tgz#39eeb36e396115cc05e29422eaea9e692c9408c7" + integrity sha512-LiWgfDLLb1dwbFQZsSglpRj+1ctGnayXz3Uv0/WO8n558JycT5fg6zkNcnW0G68Nn0aEldTFeEfmjCfmqry/rQ== + dependencies: + tslib "^1.10.0" + +map-cache@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= + +map-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= + dependencies: + object-visit "^1.0.0" + +markdown@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/markdown/-/markdown-0.5.0.tgz#28205b565a8ae7592de207463d6637dc182722b2" + integrity sha1-KCBbVlqK51kt4gdGPWY33BgnIrI= + dependencies: + nopt "~2.1.1" + +marked@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/marked/-/marked-0.7.0.tgz#b64201f051d271b1edc10a04d1ae9b74bb8e5c0e" + integrity sha512-c+yYdCZJQrsRjTPhUx7VKkApw9bwDkNbHUKo1ovgcfDjb2kc8rLuRbIFyXL5WOEUwzSSKo3IXpph2K6DqB/KZg== + +micromatch@^3.1.10, micromatch@^3.1.4: + version "3.1.10" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + braces "^2.3.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + extglob "^2.0.4" + fragment-cache "^0.2.1" + kind-of "^6.0.2" + nanomatch "^1.2.9" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.2" + +micromatch@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259" + integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q== + dependencies: + braces "^3.0.1" + picomatch "^2.0.5" + +mime-db@1.43.0, "mime-db@>= 1.43.0 < 2": + version "1.43.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.43.0.tgz#0a12e0502650e473d735535050e7c8f4eb4fae58" + integrity sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ== + +mime-types@~2.1.24: + version "2.1.26" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.26.tgz#9c921fc09b7e149a65dfdc0da4d20997200b0a06" + integrity sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ== + dependencies: + mime-db "1.43.0" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +mime@^2.4.3: + version "2.4.4" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.4.tgz#bd7b91135fc6b01cde3e9bae33d659b63d8857e5" + integrity sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA== + +min-indent@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.0.tgz#cfc45c37e9ec0d8f0a0ec3dd4ef7f7c3abe39256" + integrity sha1-z8RcN+nsDY8KDsPdTvf3w6vjklY= + +"minimatch@2 || 3", minimatch@^3.0.2, minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimist@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= + +minimist@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= + +minimist@~0.0.1: + version "0.0.10" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" + integrity sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8= + +minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" + integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== + dependencies: + safe-buffer "^5.1.2" + yallist "^3.0.0" + +minizlib@^1.2.1: + version "1.3.3" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" + integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== + dependencies: + minipass "^2.9.0" + +mixin-deep@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" + integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== + dependencies: + for-in "^1.0.2" + is-extendable "^1.0.1" + +mkdirp@0.5.x, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1, mkdirp@~0.5.x: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= + dependencies: + minimist "0.0.8" + +moment-timezone@^0.5.21: + version "0.5.27" + resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.27.tgz#73adec8139b6fe30452e78f210f27b1f346b8877" + integrity sha512-EIKQs7h5sAsjhPCqN6ggx6cEbs94GK050254TIJySD1bzoM5JTYDwAU1IoVOeTOL6Gm27kYJ51/uuvq1kIlrbw== + dependencies: + moment ">= 2.9.0" + +"moment@>= 2.9.0", moment@^2.10.6, moment@^2.22.2: + version "2.24.0" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b" + integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg== + +morgan@^1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.9.1.tgz#0a8d16734a1d9afbc824b99df87e738e58e2da59" + integrity sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA== + dependencies: + basic-auth "~2.0.0" + debug "2.6.9" + depd "~1.1.2" + on-finished "~2.3.0" + on-headers "~1.0.1" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +ms@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== + +ms@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +mv@~2: + version "2.1.1" + resolved "https://registry.yarnpkg.com/mv/-/mv-2.1.1.tgz#ae6ce0d6f6d5e0a4f7d893798d03c1ea9559b6a2" + integrity sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI= + dependencies: + mkdirp "~0.5.1" + ncp "~2.0.0" + rimraf "~2.4.0" + +nan@^2.12.1: + version "2.14.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" + integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== + +nanomatch@^1.2.9: + version "1.2.13" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" + integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + define-property "^2.0.2" + extend-shallow "^3.0.2" + fragment-cache "^0.2.1" + is-windows "^1.0.2" + kind-of "^6.0.2" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +ncp@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ncp/-/ncp-2.0.0.tgz#195a21d6c46e361d2fb1281ba38b91e9df7bdbb3" + integrity sha1-GVoh1sRuNh0vsSgbo4uR6d9727M= + +needle@^2.2.1: + version "2.4.0" + resolved "https://registry.yarnpkg.com/needle/-/needle-2.4.0.tgz#6833e74975c444642590e15a750288c5f939b57c" + integrity sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg== + dependencies: + debug "^3.2.6" + iconv-lite "^0.4.4" + sax "^1.2.4" + +negotiator@0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" + integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== + +nib@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/nib/-/nib-1.1.2.tgz#6a69ede4081b95c0def8be024a4c8ae0c2cbb6c7" + integrity sha1-amnt5AgblcDe+L4CSkyK4MLLtsc= + dependencies: + stylus "0.54.5" + +nice-try@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== + +no-case@^2.2.0: + version "2.3.2" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac" + integrity sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ== + dependencies: + lower-case "^1.1.1" + +no-case@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.3.tgz#c21b434c1ffe48b39087e86cfb4d2582e9df18f8" + integrity sha512-ehY/mVQCf9BL0gKfsJBvFJen+1V//U+0HQMPrWct40ixE4jnv0bfvxDbWtAHL9EcaPEOJHVVYKoQn1TlZUB8Tw== + dependencies: + lower-case "^2.0.1" + tslib "^1.10.0" + +node-pre-gyp@*: + version "0.14.0" + resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.14.0.tgz#9a0596533b877289bcad4e143982ca3d904ddc83" + integrity sha512-+CvDC7ZttU/sSt9rFjix/P05iS43qHCOOGzcr3Ry99bXG7VX953+vFyEuph/tfqoYu8dttBkE86JSKBO2OzcxA== + dependencies: + detect-libc "^1.0.2" + mkdirp "^0.5.1" + needle "^2.2.1" + nopt "^4.0.1" + npm-packlist "^1.1.6" + npmlog "^4.0.2" + rc "^1.2.7" + rimraf "^2.6.1" + semver "^5.3.0" + tar "^4.4.2" + +nopt@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" + integrity sha1-0NRoWv1UFRk8jHUFYC0NF81kR00= + dependencies: + abbrev "1" + osenv "^0.1.4" + +nopt@~2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-2.1.2.tgz#6cccd977b80132a07731d6e8ce58c2c8303cf9af" + integrity sha1-bMzZd7gBMqB3MdbozljCyDA8+a8= + dependencies: + abbrev "1" + +normalize-path@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= + dependencies: + remove-trailing-separator "^1.0.1" + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +npm-bundled@^1.0.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.1.tgz#1edd570865a94cdb1bc8220775e29466c9fb234b" + integrity sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA== + dependencies: + npm-normalize-package-bin "^1.0.1" + +npm-normalize-package-bin@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2" + integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA== + +npm-packlist@^1.1.6: + version "1.4.7" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.7.tgz#9e954365a06b80b18111ea900945af4f88ed4848" + integrity sha512-vAj7dIkp5NhieaGZxBJB8fF4R0078rqsmhJcAfXZ6O7JJhjhPK96n5Ry1oZcfLXgfun0GWTZPOxaEyqv8GBykQ== + dependencies: + ignore-walk "^3.0.1" + npm-bundled "^1.0.1" + +npmlog@^4.0.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" + integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== + dependencies: + are-we-there-yet "~1.1.2" + console-control-strings "~1.1.0" + gauge "~2.7.3" + set-blocking "~2.0.0" + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= + +nunjucks@^3.1.3: + version "3.2.0" + resolved "https://registry.yarnpkg.com/nunjucks/-/nunjucks-3.2.0.tgz#53e95f43c9555e822e8950008a201b1002d49933" + integrity sha512-YS/qEQ6N7qCnUdm6EoYRBfJUdWNT0PpKbbRnogV2XyXbBm2STIP1O6yrdZHgwMVK7fIYUx7i8+yatEixnXSB1w== + dependencies: + a-sync-waterfall "^1.0.0" + asap "^2.0.3" + yargs "^3.32.0" + optionalDependencies: + chokidar "^2.0.0" + +object-assign@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + +object-copy@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= + dependencies: + copy-descriptor "^0.1.0" + define-property "^0.2.5" + kind-of "^3.0.3" + +object-visit@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= + dependencies: + isobject "^3.0.0" + +object.pick@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= + dependencies: + isobject "^3.0.1" + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= + dependencies: + ee-first "1.1.1" + +on-headers@~1.0.1, on-headers@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" + integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +open@^6.3.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/open/-/open-6.4.0.tgz#5c13e96d0dc894686164f18965ecfe889ecfc8a9" + integrity sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg== + dependencies: + is-wsl "^1.1.0" + +optimist@~0.6: + version "0.6.1" + resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" + integrity sha1-2j6nRob6IaGaERwybpDrFaAZZoY= + dependencies: + minimist "~0.0.1" + wordwrap "~0.0.2" + +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= + +os-locale@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" + integrity sha1-IPnxeuKe00XoveWDsT0gCYA8FNk= + dependencies: + lcid "^1.0.0" + +os-tmpdir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= + +osenv@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" + integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.0" + +parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +pascal-case@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.1.tgz#5ac1975133ed619281e88920973d2cd1f279de5f" + integrity sha512-XIeHKqIrsquVTQL2crjq3NfJUxmdLasn3TYOU0VBM+UX2a6ztAWBlJQBePLGY7VHW8+2dRadeIPK5+KImwTxQA== + dependencies: + no-case "^3.0.3" + tslib "^1.10.0" + +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= + +path-dirname@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" + integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA= + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" + integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== + +picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.0.7: + version "2.2.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.1.tgz#21bac888b6ed8601f831ce7816e335bc779f0a4a" + integrity sha512-ISBaA8xQNmwELC7eOjqFKMESB2VIqt4PPDD0nsS95b/9dZXvVKOlz9keMSnoGGKcOHXfTvDD6WMaRoSc9UuhRA== + +posix-character-classes@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= + +pretty-hrtime@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" + integrity sha1-t+PqQkNaTJsnWdmeDyAesZWALuE= + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +punycode.js@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/punycode.js/-/punycode.js-2.1.0.tgz#f3937f7a914152c2dc17e9c280a2cf86a26b7cda" + integrity sha512-LvGUJ9QHiESLM4yn8JuJWicstRcJKRmP46psQw1HvCZ9puLFwYMKJWvkAkP3OHBVzNzZGx/D53EYJrIaKd9gZQ== + +range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +rc@^1.2.7: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +readable-stream@^2.0.2, readable-stream@^2.0.6: + version "2.3.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readdirp@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" + integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ== + dependencies: + graceful-fs "^4.1.11" + micromatch "^3.1.10" + readable-stream "^2.0.2" + +readdirp@~3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.3.0.tgz#984458d13a1e42e2e9f5841b129e162f369aff17" + integrity sha512-zz0pAkSPOXXm1viEwygWIPSPkcBYjW1xU5j/JBh5t9bGCJwa6f9+BJa6VaB2g+b55yVrmXzqkyLf4xaWYM0IkQ== + dependencies: + picomatch "^2.0.7" + +regex-not@^1.0.0, regex-not@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== + dependencies: + extend-shallow "^3.0.2" + safe-regex "^1.1.0" + +remove-trailing-separator@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= + +repeat-element@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" + integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g== + +repeat-string@^1.5.2, repeat-string@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= + +resolve-url@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= + +resolve@^1.11.0, resolve@^1.8.1: + version "1.15.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.15.0.tgz#1b7ca96073ebb52e741ffd799f6b39ea462c67f5" + integrity sha512-+hTmAldEGE80U2wJJDC1lebb5jWqvTYAfm3YZ1ckk1gBr0MnCqUKlwK1e+anaFljIl+F5tR5IoZcm4ZDA1zMQw== + dependencies: + path-parse "^1.0.6" + +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== + +rfdc@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.1.4.tgz#ba72cc1367a0ccd9cf81a870b3b58bd3ad07f8c2" + integrity sha512-5C9HXdzK8EAqN7JDif30jqsBzavB7wLpaubisuQIGHWf2gUXSpzy6ArX/+Da8RjFpagWsCn+pIgxTMAmKw9Zug== + +right-align@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" + integrity sha1-YTObci/mo1FWiSENJOFMlhSGE+8= + dependencies: + align-text "^0.1.1" + +rimraf@^2.6.1: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + +rimraf@~2.4.0: + version "2.4.5" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.4.5.tgz#ee710ce5d93a8fdb856fb5ea8ff0e2d75934b2da" + integrity sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto= + dependencies: + glob "^6.0.1" + +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-buffer@^5.1.2: + version "5.2.0" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" + integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== + +safe-json-stringify@~1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz#356e44bc98f1f93ce45df14bcd7c01cda86e0afd" + integrity sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg== + +safe-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= + dependencies: + ret "~0.1.10" + +"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sax@0.5.x: + version "0.5.8" + resolved "https://registry.yarnpkg.com/sax/-/sax-0.5.8.tgz#d472db228eb331c2506b0e8c15524adb939d12c1" + integrity sha1-1HLbIo6zMcJQaw6MFVJK25OdEsE= + +sax@^1.2.4, sax@~1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== + +semver@^5.3.0, semver@^5.5.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +semver@^6.0.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +send@0.17.1: + version "0.17.1" + resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" + integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== + dependencies: + debug "2.6.9" + depd "~1.1.2" + destroy "~1.0.4" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "~1.7.2" + mime "1.6.0" + ms "2.1.1" + on-finished "~2.3.0" + range-parser "~1.2.1" + statuses "~1.5.0" + +serve-static@^1.14.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" + integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.17.1" + +set-blocking@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + +set-value@^2.0.0, set-value@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" + integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.3" + split-string "^3.0.1" + +setprototypeof@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" + integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= + dependencies: + shebang-regex "^1.0.0" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +signal-exit@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" + integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= + +snapdragon-node@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== + dependencies: + define-property "^1.0.0" + isobject "^3.0.0" + snapdragon-util "^3.0.1" + +snapdragon-util@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== + dependencies: + kind-of "^3.2.0" + +snapdragon@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" + integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== + dependencies: + base "^0.11.1" + debug "^2.2.0" + define-property "^0.2.5" + extend-shallow "^2.0.1" + map-cache "^0.2.2" + source-map "^0.5.6" + source-map-resolve "^0.5.0" + use "^3.1.0" + +source-map-resolve@^0.5.0, source-map-resolve@^0.5.2: + version "0.5.3" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" + integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== + dependencies: + atob "^2.1.2" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + source-map-url "^0.4.0" + urix "^0.1.0" + +source-map-url@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" + integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= + +source-map@0.1.x: + version "0.1.43" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.43.tgz#c24bc146ca517c1471f5dacbe2571b2b7f9e3346" + integrity sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y= + dependencies: + amdefine ">=0.0.4" + +source-map@^0.5.6, source-map@~0.5.1: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= + +source-map@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +source-map@^0.7.3: + version "0.7.3" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" + integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== + +split-string@^3.0.1, split-string@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== + dependencies: + extend-shallow "^3.0.0" + +sprintf-js@^1.0.3: + version "1.1.2" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.2.tgz#da1765262bf8c0f571749f2ad6c26300207ae673" + integrity sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug== + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + +static-extend@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= + dependencies: + define-property "^0.2.5" + object-copy "^0.1.0" + +"statuses@>= 1.5.0 < 2", statuses@~1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= + +string-width@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +"string-width@^1.0.2 || 2": + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= + dependencies: + ansi-regex "^3.0.0" + +strip-ansi@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" + integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== + dependencies: + ansi-regex "^5.0.0" + +strip-indent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" + integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ== + dependencies: + min-indent "^1.0.0" + +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + +striptags@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/striptags/-/striptags-3.1.1.tgz#c8c3e7fdd6fb4bb3a32a3b752e5b5e3e38093ebd" + integrity sha1-yMPn/db7S7OjKjt1LltePjgJPr0= + +stylus@0.54.5: + version "0.54.5" + resolved "https://registry.yarnpkg.com/stylus/-/stylus-0.54.5.tgz#42b9560931ca7090ce8515a798ba9e6aa3d6dc79" + integrity sha1-QrlWCTHKcJDOhRWnmLqeaqPW3Hk= + dependencies: + css-parse "1.7.x" + debug "*" + glob "7.0.x" + mkdirp "0.5.x" + sax "0.5.x" + source-map "0.1.x" + +stylus@^0.54.5: + version "0.54.7" + resolved "https://registry.yarnpkg.com/stylus/-/stylus-0.54.7.tgz#c6ce4793965ee538bcebe50f31537bfc04d88cd2" + integrity sha512-Yw3WMTzVwevT6ZTrLCYNHAFmanMxdylelL3hkWNgPMeTCpMwpV3nXjpOHuBXtFv7aiO2xRuQS6OoAdgkNcSNug== + dependencies: + css-parse "~2.0.0" + debug "~3.1.0" + glob "^7.1.3" + mkdirp "~0.5.x" + safer-buffer "^2.1.2" + sax "~1.2.4" + semver "^6.0.0" + source-map "^0.7.3" + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1" + integrity sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g== + dependencies: + has-flag "^4.0.0" + +swig-extras@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/swig-extras/-/swig-extras-0.0.1.tgz#b503fede372ab9c24c6ac68caf656bcef1872328" + integrity sha1-tQP+3jcqucJMasaMr2VrzvGHIyg= + dependencies: + markdown "~0.5.0" + +swig-templates@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/swig-templates/-/swig-templates-2.0.3.tgz#6b4c43b462175df2a8da857a2043379ec6ea6fd0" + integrity sha512-QojPTuZWdpznSZWZDB63/grsZuDwT/7geMeGlftbJXDoYBIZEnTcKvz4iwYDv3SwfPX9/B4RtGRSXNnm3S2wwg== + dependencies: + optimist "~0.6" + uglify-js "2.6.0" + +tar@^4.4.2: + version "4.4.13" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525" + integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA== + dependencies: + chownr "^1.1.1" + fs-minipass "^1.2.5" + minipass "^2.8.6" + minizlib "^1.2.1" + mkdirp "^0.5.0" + safe-buffer "^5.1.2" + yallist "^3.0.3" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + +"through@>=2.2.7 <3": + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= + +tildify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/tildify/-/tildify-2.0.0.tgz#f205f3674d677ce698b7067a99e949ce03b4754a" + integrity sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw== + +titlecase@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/titlecase/-/titlecase-1.1.3.tgz#fc6d65ff582b0602410768ef1a09b70506313dc3" + integrity sha512-pQX4oiemzjBEELPqgK4WE+q0yhAqjp/yzusGtlSJsOuiDys0RQxggepYmo0BuegIDppYS3b3cpdegRwkpyN3hw== + +to-object-path@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" + integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= + dependencies: + kind-of "^3.0.2" + +to-regex-range@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= + dependencies: + is-number "^3.0.0" + repeat-string "^1.6.1" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +to-regex@^3.0.1, to-regex@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" + integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== + dependencies: + define-property "^2.0.2" + extend-shallow "^3.0.2" + regex-not "^1.0.2" + safe-regex "^1.1.0" + +toidentifier@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" + integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== + +tslib@^1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" + integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== + +uglify-js@2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.6.0.tgz#25eaa1cc3550e39410ceefafd1cfbb6b6d15f001" + integrity sha1-JeqhzDVQ45QQzu+v0c+7a20V8AE= + dependencies: + async "~0.2.6" + source-map "~0.5.1" + uglify-to-browserify "~1.0.0" + yargs "~3.10.0" + +uglify-to-browserify@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" + integrity sha1-bgkk1r2mta/jSeOabWMoUKD4grc= + +union-value@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" + integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== + dependencies: + arr-union "^3.1.0" + get-value "^2.0.6" + is-extendable "^0.1.1" + set-value "^2.0.1" + +unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= + +unset-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= + dependencies: + has-value "^0.3.1" + isobject "^3.0.0" + +upath@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" + integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== + +upper-case@^1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598" + integrity sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg= + +urix@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= + +use@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" + integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== + +util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= + +vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= + +warehouse@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/warehouse/-/warehouse-3.0.2.tgz#4c02f6fcf2b33130656f3d033c44bf5673af0433" + integrity sha512-NTaUFkDcRKFx477NflL3doMhnmPobpL+uF66s0ozAhjob+UCHcOzE77GvYR1sjyu+LR4SstPz3xGxYLOKQCvMg== + dependencies: + JSONStream "^1.0.7" + bluebird "^3.2.2" + cuid "^2.1.4" + graceful-fs "^4.1.3" + is-plain-object "^3.0.0" + rfdc "^1.1.4" + +which@^1.2.9: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +wide-align@^1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== + dependencies: + string-width "^1.0.2 || 2" + +window-size@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" + integrity sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0= + +window-size@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.4.tgz#f8e1aa1ee5a53ec5bf151ffa09742a6ad7697876" + integrity sha1-+OGqHuWlPsW/FR/6CXQqatdpeHY= + +wordwrap@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" + integrity sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8= + +wordwrap@~0.0.2: + version "0.0.3" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" + integrity sha1-o9XabNXAvAAI03I0u68b7WMFkQc= + +wrap-ansi@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" + integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU= + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +y18n@^3.2.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" + integrity sha1-bRX7qITAhnnA136I53WegR4H+kE= + +yallist@^3.0.0, yallist@^3.0.3: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + +yargs@^3.32.0: + version "3.32.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.32.0.tgz#03088e9ebf9e756b69751611d2a5ef591482c995" + integrity sha1-AwiOnr+edWtpdRYR0qXvWRSCyZU= + dependencies: + camelcase "^2.0.1" + cliui "^3.0.3" + decamelize "^1.1.1" + os-locale "^1.4.0" + string-width "^1.0.1" + window-size "^0.1.4" + y18n "^3.2.0" + +yargs@~3.10.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1" + integrity sha1-9+572FfdfB0tOMDnTvvWgdFDH9E= + dependencies: + camelcase "^1.0.2" + cliui "^2.1.0" + decamelize "^1.0.0" + window-size "0.1.0"