diff --git a/README.md b/README.md
index 4215d9a..4beca2f 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,5 @@
-# 《后端技术总结》
+
+# 《大厂面试指北》
最佳阅读地址:http://notfound9.github.io/interviewGuide/
@@ -6,25 +7,14 @@ Github项目主页:https://github.com/NotFound9/interviewGuide
作者博客地址:https://juejin.im/user/5b370a42e51d4558ce5eb969
-
## 为什么要做这个开源项目?
-之前喜欢在空闲时看一些技术书籍,但是发现看完后,即便每一章都写了读书笔记,看到一些实际的问题时,自己还是一脸茫然,所以我认为学习一项技术分为三个阶段:
-
-1.看过入门教程,会用API。
-
-2.看过相关的技术书籍,了解一部分原理。
-
-3.能够根据了解的原理对实际的问题进行分析,解决问题,这样才能对技术有较为深入的理解,在工作中遇到复杂问题时,才能解决。
-
-所以我发起了这个项目,一方面是便于自己复习巩固,一方面是将这些自己写的解答开源出来分享给大家,希望可以帮助到大家,也欢迎大家一起来完善这个项目,为开源做贡献。
-
-欢迎大家进群一起学习进步!
+之前在业余时间阅读技术书籍时,发现只阅读而不产出,这样收效甚微。所以就在网上找了很多常见的技术问题,根据自己的技术积累,查阅书籍,阅读文档和博客等资料,尝试着用自己的话去写了很多原创解答,最终整理开源到Github。一方面是便于自己复习巩固,一方面是将这些自己写的解答开源出来分享给大家,希望可以帮助到大家,也欢迎大家一起来完善这个项目,为开源做贡献。
-
+
## 目录
- [首页](README.md)
@@ -115,7 +105,7 @@ Github项目主页:https://github.com/NotFound9/interviewGuide
- [6.RedisCluster是怎么实现数据分片的?](docs/RedisUserful.md#RedisCluster是怎么实现数据分片的?)
- [7.RedisCluster是怎么做故障转移和发现的?](docs/RedisUserful.md#RedisCluster是怎么做故障转移和发现的?)
* MySQL
- - [原创面试题解答](docs/MySQLNote.md)
+ - [基础](docs/MySQLNote.md)
- [1.一条MySQL更新语句的执行过程是什么样的?](docs/MySQLNote.md#一条MySQL更新语句的执行过程是什么样的?)
- [2.脏页是什么?](docs/MySQLNote.md#脏页是什么?)
- [3.Checkpoint是什么?](docs/MySQLNote.md#Checkpoint是什么?)
@@ -146,7 +136,7 @@ Github项目主页:https://github.com/NotFound9/interviewGuide
- [28.MySQL的join的实现是怎么样的?](docs/MySQLNote.md#MySQL的join的实现是怎么样的?)
- [慢查询优化实践](docs/MySQLWork.md)
* JVM
- - [原创面试题解答](docs/JavaJVM.md)
+ - [基础](docs/JavaJVM.md)
- [1.Java内存区域怎么划分的?](docs/JavaJVM.md#Java内存区域怎么划分的?)
- [2.Java中对象的创建过程是怎么样的?](docs/JavaJVM.md#Java中对象的创建过程是怎么样的?)
- [3.Java对象的内存布局是怎么样的?](docs/JavaJVM.md#Java对象的内存布局是怎么样的?)
@@ -159,13 +149,17 @@ Github项目主页:https://github.com/NotFound9/interviewGuide
- [10.Minor GC和Full GC是什么?](docs/JavaJVM.md#MinorGC和FullGC是什么?)
- [11.如何确定一个对象可以回收?](docs/JavaJVM.md#如何确定一个对象是否可以被回收?)
- [12.目前通常使用的是什么垃圾收集器?](docs/JavaJVM.md#目前通常使用的是什么垃圾收集器?)
-- [《剑指Offer》解题思考](docs/CodingInterviews.md)
-- [《LeetCode热门100题》解题思考(上)](docs/LeetCode.md)
-- [《LeetCode热门100题》解题思考(下)](docs/LeetCode1.md)
- [Kafka](docs/Kafka.md)
- [ZooKeeper](docs/ZooKeeper.md)
- [HTTP](docs/HTTP.md)
-- [大厂面试系列](docs/BATInterview.md)
+- [Spring](docs/Spring.md)
+- [Nginx](docs/Nginx.md)
+- [系统设计](docs/SystemDesign.md)
+* 算法
+ - [《剑指Offer》解题思考](docs/CodingInterviews.md)
+ - [《LeetCode热门100题》解题思考(上)](docs/LeetCode.md)
+ - [《LeetCode热门100题》解题思考(下)](docs/LeetCode1.md)
+- [大厂面试公众号文章系列](docs/BATInterview.md)
- [【大厂面试01期】高并发场景下,如何保证缓存与数据库一致性?](https://mp.weixin.qq.com/s/hwMpAVZ1_p8gLfPAzA8X9w)
- [【大厂面试02期】Redis过期key是怎么样清理的?](https://mp.weixin.qq.com/s/J_nOPKS17Uax2zGrZsE8ZA)
- [【大厂面试03期】MySQL是怎么解决幻读问题的?](https://mp.weixin.qq.com/s/8D6EmZM3m6RiSk0-N5YCww)
diff --git a/_sidebar.md b/_sidebar.md
index 9a3f431..bd331c5 100644
--- a/_sidebar.md
+++ b/_sidebar.md
@@ -12,17 +12,21 @@
- [持久化(AOF和RDB)](docs/RedisStore.md)
- [高可用(主从切换和哨兵机制)](docs/RedisUserful.md)
* MySQL
- - [原创面试题解答](docs/MySQLNote.md)
+ - [基础](docs/MySQLNote.md)
- [慢查询优化实践](docs/MySQLWork.md)
* JVM
- - [原创面试题解答](docs/JavaJVM.md)
-- [《剑指Offer》解题思考](docs/CodingInterviews.md)
-- [《LeetCode热门100题》解题思考(上)](docs/LeetCode.md)
-- [《LeetCode热门100题》解题思考(下)](docs/LeetCode1.md)
+ - [基础](docs/JavaJVM.md)
- [Kafka](docs/Kafka.md)
- [ZooKeeper](docs/ZooKeeper.md)
- [HTTP](docs/HTTP.md)
-- [大厂面试系列](docs/BATInterview.md)
+- [Spring](docs/Spring.md)
+- [Nginx](docs/Nginx.md)
+- [系统设计](docs/SystemDesign.md)
+* 算法
+ - [《剑指Offer》解题思考](docs/CodingInterviews.md)
+ - [《LeetCode热门100题》解题思考(上)](docs/LeetCode.md)
+ - [《LeetCode热门100题》解题思考(下)](docs/LeetCode1.md)
+- [大厂面试公众号文章系列](docs/BATInterview.md)
* 读书笔记
- [《Redis设计与实现》读书笔记 上](docs/RedisBook1.md)
- [《Redis设计与实现》读书笔记 下](docs/RedisBook2.md)
diff --git a/docs/ArrayList.md b/docs/ArrayList.md
index 14191a6..d2717bb 100644
--- a/docs/ArrayList.md
+++ b/docs/ArrayList.md
@@ -17,7 +17,7 @@
#### 1.底层使用的数据结构
-* Arraylist 底层使用的是**Object数组**,初始化时就会指向的会是一个static修饰的空数组,数组长度一开始为**0**,插入第一个元素时数组长度会初始化为**10**,之后每次数组空间不够进行扩容时都是增加为原来的**1.5倍**。ArrayList的空间浪费主要体现在在list列表的结尾会预留一定的容量空间(为了避免添加元素时,数组空间不够频繁申请内存),而LinkedList的空间花费则体现在它的每一个元素都需要消耗比ArrayList更多的空间(因为要存放后继指针next和前驱指针pre以及数据)
+* ArrayList 底层使用的是**Object数组**,初始化时就会指向的会是一个static修饰的空数组,数组长度一开始为**0**,插入第一个元素时数组长度会初始化为**10**,之后每次数组空间不够进行扩容时都是增加为原来的**1.5倍**。ArrayList的空间浪费主要体现在在list列表的结尾会预留一定的容量空间(为了避免添加元素时,数组空间不够频繁申请内存),而LinkedList的空间花费则体现在它的每一个元素都需要消耗比ArrayList更多的空间(因为要存放后继指针next和前驱指针pre以及数据)
* LinkedList 底层使用的数据结构是**双向链表**,每个节点保存了指向前驱节点和后继结点的指针。初始化时,不执行任何操作,添加第一个元素时,再去构造链表中的节点。
@@ -95,7 +95,7 @@ static class SynchronizedList
mutex = this;//mutex就是SynchronizedList实例自己,作为同步锁使用
}
- public E get(int index) {
+ public E get(int index) {
synchronized (mutex) {
是父类中的成员变量,在父类中会将list赋值给mutex
return list.get(index);
diff --git a/docs/CodingInterviews.md b/docs/CodingInterviews.md
index d93446e..bc294f1 100644
--- a/docs/CodingInterviews.md
+++ b/docs/CodingInterviews.md
@@ -170,8 +170,6 @@
}
```
-
-
## 题005 从尾到头打印链表
输入一个链表,按链表值从尾到头的顺序返回一个ArrayList。
@@ -229,6 +227,7 @@ public ArrayList printListFromTailToHead(ListNode listNode) {
int leftLength = i - inStart;//左子树长度
treeNode.left = reConstructBinaryTree(pre, preStart + 1, preStart+leftLength, in, inStart, i-1);
treeNode.right = reConstructBinaryTree(pre, preStart +leftLength+1, preEnd, in, i+1, inEnd);
+ break;
}
}
return treeNode;
@@ -244,11 +243,9 @@ public ArrayList printListFromTailToHead(ListNode listNode) {
```java
Stack stack1 = new Stack();
Stack stack2 = new Stack();
-
public void push(Integer number) {
stack1.push(number);
}
-
public Integer pop() {
if (stack2.size()>0) {
return stack2.pop();
@@ -291,9 +288,9 @@ int minNumberInRotateArray(int[] array) {
}
return min;
}
- if ( array[mid]>=array[start]){
+ if ( array[mid]>=array[start]){//左半部分是递增的,那么就去掉左半部分
start = mid;
- } else if(array[mid]<=array[end]) {
+ } else if(array[mid]<=array[end]) {//右半部分是递增的,那么就去掉右半部分
end = mid;
}
}
@@ -898,13 +895,14 @@ public static boolean VerifySquenceOfBST(int[] sequence, int start, int end) {
} else if(rightChildIndex== start) {//说明全部位于右边子树
return VerifySquenceOfBST(sequence,start,end-1);
}
+ //继续校验
return VerifySquenceOfBST(sequence,start,rightChildIndex-1) && VerifySquenceOfBST(sequence,rightChildIndex, end-1);
}
```
## 题023 二叉树中和为某一值的路径
-输入一颗二叉树的跟节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的 list 中,数组长度大的数组靠前)
+输入一颗二叉树的根节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的 list 中,数组长度大的数组靠前。)
就是递归调用每个节点的左右子树,然后将节点值相加,如果节点值和为某个预期值,并且该节点为叶子节点,那么这条路径就是要找的路径。
@@ -1070,8 +1068,6 @@ public RandomListNode Clone(RandomListNode pHead)
}
```
-
-
## 题027数组中出现的次数超过一半的数字
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出 2 。如果不存在则输出 0 。
@@ -1118,7 +1114,7 @@ public int MoreThanHalfNum_Solution(int [] array) {
输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4。
-其实就是插入排序,只不过只是对k个数进行插入排序
+其实就是插入排序,只不过只是对k个数进行插入排序,但是这样的时间复杂度会是O(N^2),如果是使用快排来做,平均时间复杂度就是O(N)。
```java
public ArrayList GetLeastNumbers_Solution(int [] input, int k) {
@@ -1153,13 +1149,13 @@ public ArrayList GetLeastNumbers_Solution(int [] input, int k) {
## 题029连续子数组的最大和
-例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。给一个数组,返回它的最大连续子序列的和,你会不会被他忽悠住?(子向量的长度至少是1)
+例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第1个开始,到第4个为止)。给一个数组,返回它的最大连续子序列的和,你会不会被他忽悠住?(子向量的长度至少是1)
使用动态规划的方法来进行思考
f(n) 有两种取值
-* 当f(n-1)<=0时,取array[n]
+* 当f(n-1)<=0时,取array[n],从这个元素重新开始
* 当f(n-1)>0时,取f(n-1)+array[n]
@@ -1172,7 +1168,7 @@ f(n) 有两种取值
int currentSum = array[0];
int maxSum = currentSum;
for (int i = 1; i < array.length; i++) {
- if (currentSum<0) {
+ if (currentSum<0) {//前面的和是负数,就直接丢弃
currentSum = array[i];
} else {
currentSum = currentSum + array[i];
@@ -1621,7 +1617,7 @@ public int IsBalanced_Solution_Depth(TreeNode root) {
int right = IsBalanced_Solution_Depth(root.right);
if (left!=-1 && right!= -1) {
int temp = left-right;
- if (temp<=1&& temp>=-1) {
+ if (temp<=1 && temp>=-1) {
return left > right ? left + 1 : right + 1;
}
}
@@ -1871,7 +1867,7 @@ public static void qsort(int[] array, int start ,int end) {
在n个人中间报数时,每个人的编号是
0 1 2 ... k k+1 k+2 ... n-1
当k出局以后,在n-1个人中报数时,每个人的编号是重新从k+1开始计数,原来的编号就映射为下面这样了(原来k+1变成了0,原来的n-1变成了n-1-(k+1))
-n-k+1 ... 0 1 ... n-1 -(k+1)
+n-k-1 ... 0 1 ... n-1 -(k+1)
所以假设从n-1报数时的编号到n个人报数时的编号存在一个映射关系
假设f(n)代表n个人报数编号
f(n) = (f(n-1)+k+1)%n = (f(n-1)+ (m-1)%n + 1)%n =
@@ -1959,7 +1955,7 @@ public int Add(int num1,int num2) {
-2147483648 至 2147483647
--2的31次方 2的31次方-1
+-2的31次方 2的31次方 -1
```java
public static int StrToInt(String str) {
@@ -2040,12 +2036,14 @@ public int[] multiply(int[] A) {
int[] d = new int[A.length];
c[0] = 1;
int result1 = c[0];
+ //构建数组C
for (int i = 1; i < c.length; i++) {
result1 = result1 * A[i-1];
c[i] = result1;
}
d[d.length-1] = 1;
int result2 = 1;
+ //构建数组D
for (int i = d.length-2; i >=0; i--) {
result2 = result2 * A[i+1];
d[i] = result2;
@@ -2073,15 +2071,15 @@ public int[] multiply(int[] A) {
}
public char FirstAppearingOnce()
- {
- for (int i = 0; i < str.length(); i++) {
- char c = str.charAt(i);
- if (table[c] == 1) {
- return c;
- }
- }
- return '#';
+{
+ for (int i = 0; i < str.length(); i++) {
+ char c = str.charAt(i);
+ if (table[c] == 1) {
+ return c;
+ }
}
+ return '#';
+}
```
## 题054 链表中环的入口节点
@@ -2091,11 +2089,11 @@ public char FirstAppearingOnce()
一种方法是遍历整个链表,将每个节点添加到HashSet中,判断是否在HashSet中出现过,第一个重复的节点就是环的入口节点。
-另一种解决方法是,假设存在环,环的长度为x,第一个指针先走x步,然后第二个指针从链表头结点出发,两个指针一起走,当第而个指针刚好走到环入口时,第一个指针正好在环中走了一圈,也在环的入口,此时的节点就是环的的入口节点,
+另一种解决方法是,假设存在环,环的长度为x,第一个指针先走x步,然后第二个指针从链表头结点出发,两个指针一起走,当第二个指针刚好走到环入口时,第一个指针正好在环中走了一圈,也在环的入口,此时的节点就是环的的入口节点,
怎么得到环的长度呢,就是一个指针每次走2步,一个指针每次走一步,他们相遇时的节点肯定就是在环中的某个节点,然后这个节点在环中遍历一圈,回到原点,就可以得到环的长度count。
-两个指针从头出发,第一个指针先走count步,然后两个指针每次都只走一步,相遇的地方就是环的入口,
+两个指针从头出发,第一个指针先走count步,然后两个指针每次都只走一步,相遇的地方就是环的入口。
```java
public ListNode EntryNodeOfLoop(ListNode pHead)
@@ -2196,7 +2194,6 @@ public ListNode deleteDuplication(ListNode pHead)
ListNode ourHead = new ListNode(0);
ourHead.next = pHead;
- int temp = pHead.val;
ListNode preNode = ourHead;
ListNode currentNode = ourHead.next;
@@ -2332,7 +2329,7 @@ public ArrayList> Print(TreeNode pRoot) {
int flag = 0;//代表当前遍历的是奇数层还是偶数层。区别在于添加子节点的顺序。
stack1.add(pRoot);
while ((flag == 0 && stack1.size()>0) || (flag == 1 && stack2.size()>0)) {
- if (flag==0) {
+ if (flag==0) {//代表是偶数层
ArrayList array = new ArrayList();
while (stack1.size()>0) {
TreeNode node = stack1.pop();
@@ -2442,7 +2439,7 @@ ArrayList> Print(TreeNode pRoot) {
就是递归遍历每一个节点,遍历时传入深度depth,将节点加入到ArrayList中特定深度对应的数组中去。
-这种方法也可以用来进行二叉树深度遍历,遍历完之后将嵌套数组拆分成单层的数组。
+这种方法也可以用来进行二叉树先序遍历,遍历完之后将嵌套数组拆分成单层的数组。
```java
ArrayList> Print2(TreeNode pRoot) {
@@ -2741,7 +2738,7 @@ boolean judge(char[] matrix, int rows, int cols, int i, int j, char[] str, int c
return false;
}
if (charIndex==str.length-1) { return true;}
-
+ //用来记录这个位置是否在路径中
flag[index]=true;
if (judge(matrix,rows,cols,i+1, j, str, charIndex+1, flag)
||judge(matrix,rows,cols,i-1, j, str, charIndex+1, flag)
@@ -2849,10 +2846,6 @@ public int cutRope(int target) {
}
```
-
-
-
-
# 基础篇
## 二分查找
diff --git a/docs/Git.md b/docs/Git.md
new file mode 100644
index 0000000..83ed882
--- /dev/null
+++ b/docs/Git.md
@@ -0,0 +1,126 @@
+##### git命令使用
+
+`git branch dev` 创建一个名称为dev的分支
+
+`git checkout dev`切换到dev分支上
+
+
+`git checkout -b dev ` 从当前分支上创建出一个新分支dev
+
+
+`git merge dev` 假设当前为master分支,执行这条命令后会将dev分支合并到当前master分支,合并完成后,master分支和dev分支会变成同一个分支
+
+`git:(dev) git rebase master` 假设当前为dev分支,git rebase master命令会将当dev的更改进行回退,回退到与main分支交汇的地方,将这些更改暂存到rebase目录,然后将master上面的提交拿过来,让dev分支更新到master分支那样,然后再把之前暂存的更改应用上,中间如果出现冲突,需要解决冲突,解决完冲突后,使用git add 将更新添加上,然后使用git rebase --continue继续合并。如果中间需要中断合并那么可以使用git rebase —abort。
+
+在rebase完成后,我们dev分支已经是最新了,但是master上还是老的代码,我们需要使用git checkout master 切换到master分支上,然后使用git rebase dev将dev上面的更改移动到master上来,然后push到远程。
+
+
+`git checkout HEAD^` 将当前head指向上1次提交
+
+`git checkout HEAD~3` 将当前head指向上3次提交
+
+`git reset HEAD~1` 回滚到上一次提交
+
+`git reset HEAD` 生成一个新的提交,用于将上一个提交的更改撤销掉,等价于`git reset HEAD~1`
+
+`git cherry-pick C3 C4 C7`将其他分支上的C3提交,C4提交,C7提交拿到这个分支上面来
+
+`git rebase -i HEAD~3` 合并最近的两次提交为一次提交记录,执行这个命令后会进入vim界面,然后会出现3次提交的记录
+
+```java
+pick 115e825 queryCreditsMonth
+pick 4cedfe6 queryCreditsMonth
+pick b3dccfd nickname
+```
+
+有一下操作符:
+pick:保留该commit(缩写:p)
+reword:保留该commit,但我需要修改该commit的注释(缩写:r)
+edit:保留该commit, 但我要停下来修改该提交(不仅仅修改注释)(缩写:e)
+squash:将该commit和前一个commit合并(缩写:s)
+fixup:将该commit和前一个commit合并,但我不要保留该提交的注释信息(缩写:f)
+exec:执行shell命令(缩写:x)
+drop:我要丢弃该commit(缩写:d)
+
+咱们如果要把三次提交合并成一个就改成这样
+
+```
+pick 115e825 queryCreditsMonth
+s 4cedfe6 queryCreditsMonth
+s b3dccfd nickname
+```
+
+然后保存退出,然后会进入填写commit说明的地方,我们直接保存就好了,这样就完成了,会生成一个新的commit
+
+```java
+commit b3dccfd2c2173fa0a6358de604b6541c8c6c644a (HEAD -> feature-dev)
+Date: Fri May 7 16:29:22 2021 +0800
+
+ nickname
+
+ nickname
+
+ change credits
+```
+
+
+
+`git commit -amend`可以修改最后一次提交的commit说明
+
+
+
+
+
+
+
+##### rebase出现了冲突怎么办?
+
+假设master上跟我们改了同一行,其实就会造成冲突
+例如下面这个就是我们需要解决的冲突,<<<<< HEAD 到====== 之间为其他分支上别人的更改,======到>>>>>>> change credits之间为我们自己在dev分支上的更改,我们需要解决冲突,然后使用git add对文件进行提交
+```java
+ @Override
+ public List creditsMonthsQuery(Integer months, long year) {
+<<<<<<< HEAD
+ List creditsMonth = creditsMonthsMapper.creditsMonthsQuery(months, year);
+ for(CreditsMonthsUser creditsMonthsUser : creditsMonth){
+ String nickname = WechatUtil.decodeNickName(creditsMonthsUser.getNickname());
+ creditsMonthsUser.setNickname(nickname);
+ }
+ return creditsMonth;
+=======
+ Integer a = 100;
+ return creditsMonthsMapper.creditsMonthsQuery(months, year);
+>>>>>>> change credits
+ }
+
+```
+
+##### 日常开发流程应该是怎么样的呢?
+1. git fetch origin dev 拉取远程的dev分支到本地,本地也会创建出dev分支
+
+2.git checkout -b feature-dev 我们自己从dev分支创建出一个feature-dev分支,用于我们自己开发
+
+3.我们自己在feature-dev开发了一些功能后,进行提交时,可能其他人往dev分支上提交了更新,我们需要将更新拿到本地
+
+4.
+
+git checkout dev 切到dev分支
+git pull origin dev 将其他人在远程dev上的提交拉到本地
+
+5.
+
+git checkout feature-dev
+
+git rebase dev 将dev上的更改应用到我们的feature-dev分支
+
+然后可能会出现冲突,我们对冲突进行合并,
+
+然后对修改后的文件使用git add +文件名 进行添加,
+
+添加完成后使用git rebase --continue就可以继续,然后合并完成(如果需要中断rebase操作可以使用git rebase --abort)
+
+6.git checkout feature-dev
+
+git rebase feature-dev 就可以把feature-dev上合并好的更改拿到dev分支上
+
+7. git push origin dev 就可以将dev分支上的更改提交到远程
\ No newline at end of file
diff --git a/docs/HTTP.md b/docs/HTTP.md
index eac8df1..fbd2a92 100644
--- a/docs/HTTP.md
+++ b/docs/HTTP.md
@@ -5,14 +5,13 @@
#### [3. TCP三次握手和四次挥手是怎么样的?](#TCP三次握手和四次挥手是怎么样的?)
#### [4.TCP怎么保证可靠性的?](#TCP怎么保证可靠性的?)
#### [5.TCP拥塞控制怎么实现?](#TCP拥塞控制怎么实现?)
-#### [6.close_wait 太多怎么处理?为什么会出现这种情况?](#close_wait 太多怎么处理?为什么会出现这种情况?)
-
+#### [6.close_wait太多怎么处理?](#close_wait太多怎么处理?)
### HTTPS建立连接的过程是怎么样的?

-在发起连接之前,服务器会向证书机构申请SSL证书,流程是服务器将自己的公钥发给CA证书机构,CA证书机构会用自己的私钥对服务器的公钥加密,生成CA证书给服务器,服务器将SSL证书存储后供之后建立安全连接使用。
+在发起连接之前,服务器会向证书机构申请SSL证书,流程是服务器将自己的公钥发给CA证书机构,CA证书机构会用自己的私钥对服务器的公钥加密,生成SSL证书给服务器,服务器将SSL证书存储后供之后建立安全连接使用。
1.客户端发起请求,TCP三次握手,跟服务器建立连接。
如上图所示,在第 ② 步时服务器发送了一个SSL证书给客户端,SSL 证书中包含的具体内容有:
@@ -175,6 +174,10 @@ TCP连接刚建立,一点一点地提速,试探一下网络的承受能力
一旦认定网络拥塞,就会将慢启动阀值ssthresh设置为发生拥塞时的窗口cwnd大小的一半。
+PS:快重传是什么?
+
+假设发送方发送了1,2,3,4个数据包,假设接收方收到1,2数据包,并且发送了ACK确认,没有收到3,但是收到了4。根据可靠性传输原理,由于没有收到3,即便接受到了4,它也是一个失序报文,接收方不能对它进行确认。只能等发送方在等待3的ACK的回应时间超过2MSL后,进行重发。但是在这里为了让发送方快速知道哪些数据报文丢失了,接收方在收到3时就会给他返回2的ACK,一旦收到3个重复的ACK回应,也就是2的ACK,发送方就会意识到数据包3丢了,就会进行快速重传,重发报文3。
+
**快速恢复算法**
发生网络拥塞后,慢启动阀值ssthresh设置为发生拥塞时的窗口cwnd大小的一半后,
@@ -183,7 +186,7 @@ TCP连接刚建立,一点一点地提速,试探一下网络的承受能力
如果现在的TCP Reno算发,会将cwnd窗口设置为新的ssthresh值的大小,后续开始进入拥塞避免算法的流程,对cwnd窗口进入线性增长的状态。
-### close_wait 太多怎么处理?为什么会出现这种情况?
+### close_wait太多怎么处理?
close_wait 主要在TCP四次挥手时,服务端给客户端返回ACK应答后,由于自身还需要给客户端传输数据,所以会进入到close_wait状态,直到不需要给客户端发数据了,才会去给客户端发送FIN包,同时进入LAST_ACK状态。(被动关闭的一方没有及时发出 FIN 包就会导致自己一直处于close_wait状态。)
@@ -222,9 +225,13 @@ HTTP/2并没有使用传统的压缩算法,而是开发了专门的"HPACK”
#### 3.多路复用
在 HTTP/2 中引入了多路复用的技术。多路复用很好的解决了浏览器限制同一个域名下的请求数量的问题,同时也接更容易实现全速传输,毕竟新开一个 TCP 连接都需要慢慢提升传输速度。
+https://my.oschina.net/u/4331678/blog/3628959
+
#### 4.Server Push
+
HTTP2还在一定程度上改变了传统的“请求-应答”工作模式,服务器不再是完全被动地响应请求,也可以新建“流”主动向客户端发送消息。比如,在浏览器刚请求HTML的时候就提前把可能会用到的JS、CSS文件发给客户端,减少等待的延迟,这被称为"服务器推送"( Server Push,也叫 Cache push)
#### 5.提高安全性
+
出于兼容的考虑,HTTP/2延续了HTTP/1的“明文”特点,可以像以前一样使用明文传输数据,不强制使用加密通信,不过格式还是二进制,只是不需要解密。
但由于HTTPS已经是大势所趋,而且主流的浏览器Chrome、Firefox等都公开宣布只支持加密的HTTP/2,所以“事实上”的HTTP/2是加密的。也就是说,互联网上通常所能见到的HTTP/2都是使用"https”协议名,跑在TLS上面。HTTP/2协议定义了两个字符串标识符:“h2"表示加密的HTTP/2,“h2c”表示明文的HTTP/2。
@@ -343,3 +350,10 @@ Connection: 连接方式。

+##### HTTP状态码502,503,504各自代表着什么?
+
+502是指网关(一般是Nginx)从后端服务器接受到了无效的响应结果。通常是我们的后端服务器挂了之类的。
+
+503是请求过载,就是请求超过了Nginx限流设置的阀值,就会返回503服务不可用。
+
+504是指网关(一般是Nginx)从后端服务器接受的响应超时了。
\ No newline at end of file
diff --git a/docs/HashMap.md b/docs/HashMap.md
index 01b9fd6..95ae725 100644
--- a/docs/HashMap.md
+++ b/docs/HashMap.md
@@ -502,7 +502,7 @@ table = newTab;
hash%length=hash&(length-1)
```
-而因为length是2的N次幂,length-1在二进制中其实是N-1个1。例如:
+而因为length是2的N次幂,length-1在二进制中其实是N个1。例如:
length为16,length用2进制表示是10000,
@@ -929,3 +929,81 @@ void afterNodeAccess(Node e) { // move node to last
}
}
```
+
+### JDK1.7的HashMap链表成环是怎么造成的?
+
+这是JDK1.7里面HashMap扩容相关的代码:
+
+```java
+void transfer(Entry[] newTable, boolean rehash) {
+ int newCapacity = newTable.length;
+ for (Entry e : table) {//遍历原数组的每一个下标
+ while(null != e) {
+ //将数组下标下所有元素一个一个迁移到新数组,使用头插法
+ Entry next = e.next;
+ if (rehash) {
+ e.hash = null == e.key ? 0 : hash(e.key);
+ }
+ int i = indexFor(e.hash, newCapacity);
+ e.next = newTable[i];
+ newTable[i] = e;
+ e = next;
+ }
+ }
+}
+```
+
+JDK1.7版本的HashMap中,两个线程同时添加一个新的键值对,然后同时触发扩容时,两个线程都会进行扩容,就会造成前一个线程将某个数组下标的元素迁移过去后,另一个线程又进行迁移。假设原数组下标下有node1->node2->null,这样一个链表,线程A和线程B都在迁移,都拿到了node1,假设线程A先执行,将两个节点迁移到新的数组,假设node1和node2在新数组还是在同一下标下,那么迁移后的链表是node2->node1->null,此时如果线程B还在迁移,拿到node1又迁移,会让node1-next=node,从而让node1和node2形成环。
+
+##### 那么JDK1.8版本的HashMap是怎么解决的呢?
+
+首先迁移时不是拿到一个键值对就迁移一个了,而是对一个数组下标下的链表进行遍历,根据hash值的不同,分成两个链表,然后将两个链表分别设置到新的数组的下标下。
+
+```java
+if (oldTab != null) {
+ for (int j = 0; j < oldCap; ++j) {
+ Node e;
+ if ((e = oldTab[j]) != null) {
+ oldTab[j] = null;
+ if (e.next == null)
+ newTab[e.hash & (newCap - 1)] = e;
+ else if (e instanceof TreeNode)
+ ((TreeNode)e).split(this, newTab, j, oldCap);
+ else { // preserve order
+ Node loHead = null, loTail = null;
+ Node hiHead = null, hiTail = null;
+ Node next;
+ do {
+ next = e.next;
+ if ((e.hash & oldCap) == 0) {
+ if (loTail == null)
+ loHead = e;
+ else
+ loTail.next = e;
+ loTail = e;
+ }
+ else {
+ if (hiTail == null)
+ hiHead = e;
+ else
+ hiTail.next = e;
+ hiTail = e;
+ }
+ } while ((e = next) != null);
+ if (loTail != null) {
+ loTail.next = null;
+ newTab[j] = loHead;
+ }
+ if (hiTail != null) {
+ hiTail.next = null;
+ newTab[j + oldCap] = hiHead;
+ }
+ }
+ }
+ }
+ }
+```
+
+
+
+https://zhuanlan.zhihu.com/p/111501405
\ No newline at end of file
diff --git a/docs/JavaBasic.md b/docs/JavaBasic.md
index b61ec14..195c533 100644
--- a/docs/JavaBasic.md
+++ b/docs/JavaBasic.md
@@ -515,7 +515,7 @@ public String toString() {
#### wait()方法和notify()方法
-```
+```java
//timeout是超时时间,也就是等待的最大毫秒数,如果为0,代表会一直等待下去
public final native void wait(long timeout) throws InterruptedException;
public final native void notify();
@@ -693,7 +693,7 @@ Error就是一些程序处理不了的错误,代表JVM出现了一些错误,
RuntimeException异常就是应用程序运行时,可能会抛出的异常。这些异常是非受检异常,编译时Java编译器不会去检查,不会强制程序员添加处理异常的代码。程序中可以选择捕获处理,也可以不处理。如NullPointerException(空指针异常)、IndexOutOfBoundsException(下标越界异常)等。
-非受检异常以外的异常可以认为是受检异常,从程序语法角度讲是必须进行处理的异常,编译时编译器就会要求有相关的异常捕获处理的代码逻辑。如IOException、SQLException。
+非受检异常以外的异常可以认为是受检异常,从程序语法角度讲是必须进行处理的异常,编译时编译器就会要求有相关的异常捕获处理的代码逻辑。如IOException、SQLException,FileNotFoundException。
##### PS:
@@ -715,4 +715,14 @@ RuntimeException异常就是应用程序运行时,可能会抛出的异常。
### 包装类型和基本类型的区别是什么?
-最主要的区别是包装类型是对象,拥有字段和方法,可以很方便地调用一些基本的方法,初始值是null,而且可以使用null代表空值,而基本数据类型只能使用0来代表初始值。其次是基本数据类型是直接存储在栈中,而包装类型是一个对象,对象的引用变量是存储在栈中,存储了对象在堆中的地址,对象的数据是存在堆中。
\ No newline at end of file
+最主要的区别是包装类型是对象,拥有字段和方法,可以很方便地调用一些基本的方法,初始值是null,而且可以使用null代表空值,而基本数据类型只能使用0来代表初始值。其次是基本数据类型是直接存储在栈中,而包装类型是一个对象,对象的引用变量是存储在栈中,存储了对象在堆中的地址,对象的数据是存在堆中。
+
+### JDK8 Stream与普通for循环的区别?
+
+对于简单操作,比如最简单的遍历,Stream串行api性能明显差于显示迭代,但并行的Stream API能够发挥多核特性。
+
+元素为整型时,普通for循环耗时:串行api耗时:双核CPU并行Stream API=1:2:1
+
+元素为字符串型时,普通for循环耗时:串行api耗时:双核CPU并行Stream API=1:1.5:0.72。
+
+https://blog.csdn.net/zhenghongcs/article/details/104305798
\ No newline at end of file
diff --git a/docs/JavaJVM.md b/docs/JavaJVM.md
index 207ac62..fbf2eec 100644
--- a/docs/JavaJVM.md
+++ b/docs/JavaJVM.md
@@ -42,9 +42,9 @@
堆存储了几乎所有对象实例和数组,是被所有线程进行共享的区域。在逻辑上是连续的,在物理上可以是不连续的内存空间(在存储一些类似于数组的这种大对象时,基于简单和性能考虑会使用连续的内存空间)。
#### 方法区
-存储了被虚拟机加载的类型信息,常量,静态变量等数据,在JDK8以后,存储在元空间中(以前是存储在堆中的永久代中,JDK8以后已经没有永久代了)。
+存储了被虚拟机加载的**类型信息**,**常量**,**静态变量**等数据,在JDK8以后,存储在**方法区的元空间**中(以前是存储在堆中的永久代中,JDK8以后已经没有永久代了)。
-运行时常量池是方法区的一部分,会存储各种字面量和符号引用。具备动态性,运行时也可以添加新的常量入池(例如调用String的intern()方法时,如果常量池没有相应的字符串,会将它添加到常量池)。
+**运行时常量池**是方法区的一部分,会存储各种字面量和符号引用。具备动态性,运行时也可以添加新的常量入池(例如调用String的intern()方法时,如果常量池没有相应的字符串,会将它添加到常量池)。
### 直接内存区(不属于虚拟机运行时数据区)
@@ -125,7 +125,7 @@

- **Class Pointer**:用来指向对象对应的Class对象(其对应的元数据对象)的内存地址。在32位系统占4字节,在64位系统中占8字节。
+ **Class Pointer**:用来指向对象对应的Class对象(其对应的元数据对象)的内存地址。在开启了指针压缩时,占4字节。(默认是开启的)
**Length**:如果是数组对象,还有一个保存数组长度的空间,占4个字节。
@@ -137,6 +137,10 @@
因为HotSpot虚拟机的自动内存管理系统要求对象起始地址是8字节的整数倍,所以任何对象的大小必须是8字节的整数倍,而对象头部分一般是8字节的倍数,如果实力数据部分不是8字节的整数倍,需要对齐填充来补全。
+### 如何计算一个对象在内存中占多少个字节?
+
+由于默认是开启了指针压缩的,现在普遍的机器位数都是64位,如果对象是普通对象,非数组类型,那么就是对象头部分就是12字节(类型指针4字节,Mark Word 8字节),假设这个对象只有一个Integer变量,那么在实例数据部分就是一个引用变量的空间4字节,总共是16字节,由于正好是8的倍数,就不需要进行对齐填充了。
+
### 垃圾回收有哪些特点?
@@ -584,13 +588,15 @@ Minor GC:对新生代进行回收,不会影响到年老代。因为新生代
Full GC:也叫 Major GC,对整个堆进行回收,包括新生代和老年代。由于Full GC需要对整个堆进行回收,所以比Minor GC要慢,因此应该尽可能减少Full GC的次数,导致Full GC的原因包括:老年代被写满和System.gc()被显式调用等。
### 触发Minor GC的条件有哪些?
+
1.为新对象分配内存时,新生代的Eden区空间不足。
新生代回收日志:
-```
+```java
2020-05-12T16:15:10.736+0800: 7.803: [GC (Allocation Failure) 2020-05-12T16:15:10.736+0800: 7.803: [ParNew: 838912K->22016K(943744K), 0.0982676 secs] 838912K->22016K(1992320K), 0.0983280 secs] [Times: user=0.19 sys=0.01, real=0.10 secs]
```
### 触发Full GC的条件有哪些?
+
主要分为三种:
#### 1.system.gc()
@@ -1178,7 +1184,7 @@ class name列:表示的就是当前类的名称,class name 对于基本数
##### jmap -dump
-使用`jmap -dump:format=b,file=dump.hprof 进程id`可以生成当前的堆栈快照,堆快照和对象统计信息,对生成的堆快照进行分析,可以分析堆中对象所占用内存的情况,检查大对象等。执行`jvisualvm`命令打开使用Java自带的工具Java VisualVM来打开堆栈快照文件,进行分析。可以用于排查内存溢出,内存泄露问题。在Java VisualVM里面可以看到每个类的实例对象占用的内存大小,以及持有这个对象的实例所在的类等等信息。
+使用`jmap -dump:format=b,file=dump.hprof 进程id`可以生成当前的堆栈快照,堆快照和对象统计信息,对生成的堆快照进行分析,可以分析堆中对象所占用内存的情况,检查大对象等。执行`jvisualvm`命令打开使用Java自带的工具**Java VisualVM**来打开堆栈快照文件,进行分析。可以用于排查内存溢出,内存泄露问题。在**Java VisualVM**里面可以看到每个类的实例对象占用的内存大小,以及持有这个对象的实例所在的类等等信息。
也可以配置启动时的JVM参数,让发送内存溢出时,自动生成堆栈快照文件。
@@ -1266,3 +1272,27 @@ public class Test018 {

+### happens-before指的是什么?
+happens-before主要是为保证Java多线程操作时的有序性和可见性,防止了编译器重排序对程序结果的影响。
+因为当一个变量出现一写多读,多读多写的情况时,就是多个线程读这个变量,然后至少一个线程写这个变量,因为编译器在编译时会对指令进行重排序,如果没有happens-before原则对重排序进行一些约束,就有可能造成程序执行不正确的情况。
+
+具体有8条规则:
+1.**程序次序性规则**:一个线程内,按照代码顺序,书写在前面的操作先行发生于书写在后面的操作。一段代码在单线程中执行的结果是有序的。(只在单线程有效,一写多读时,写的变量如果是没有使用volatile修饰时,是没法保证其他线程读到的变量是最新的值)
+
+2.**锁定规则**:一个锁的解锁操作总是要在加锁操作之前。
+
+3.**volatile规则**:如果一个写操作去写一个volatile变量,一个读操作去读volatile变量,那么写操作一定是在读操作之前。
+
+4.**传递规则**:操作A happens before 操作B, B happens before C,那么A一定是happens before C的。
+
+5.**线程启动规则**:线程A执行过程中修一些共享变量,然后再调用threadB.start()方法来启动线程B,那么A对那些变量的修改对线程B一定是可见的。
+
+6.**线程终结规则**:线程A执行过程中调用了threadB.join()方法来等待线程B执行完毕,那么线程B在执行过程中对共享变量的修改,在join()方法返回后,对线程A可见。
+
+7.**线程中断规则**:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生。
+
+8.**对象终结规则**:一个对象的初始化完成先行发生于他的finalize()方法的开始;
+
+https://www.cnblogs.com/chenssy/p/6393321.html
+
+http://ifeve.com/java-%e4%bd%bf%e7%94%a8-happen-before-%e8%a7%84%e5%88%99%e5%ae%9e%e7%8e%b0%e5%85%b1%e4%ba%ab%e5%8f%98%e9%87%8f%e7%9a%84%e5%90%8c%e6%ad%a5%e6%93%8d%e4%bd%9c/#more-38824
\ No newline at end of file
diff --git a/docs/JavaMultiThread.md b/docs/JavaMultiThread.md
index 188ccfa..ba3743f 100644
--- a/docs/JavaMultiThread.md
+++ b/docs/JavaMultiThread.md
@@ -3,6 +3,7 @@
#### [1.进程与线程的区别是什么?](#进程与线程的区别是什么?)
#### [2.进程间如何通信?](#进程间如何通信?)
+
#### [3.Java中单例有哪些写法?](#Java中单例有哪些写法?)
#### [4.Java中创建线程有哪些方式?](#Java中创建线程有哪些方式?)
#### [5.如何解决序列化时可以创建出单例对象的问题?](#如何解决序列化时可以创建出单例对象的问题?)
@@ -54,15 +55,15 @@ Linux系统会给每个进程分配4G的虚拟地址空间(0到3G是User地址
五态模型一般指的是:
-新建态(创建一个进程)
+**新建态**(创建一个进程)
-就绪态(已经获取到资源,准备好了,进入运行队列,一旦获得时间片可以立即执行)
+**就绪态**(已经获取到资源,准备好了,进入运行队列,一旦获得时间片可以立即执行)
-运行态(获取到了时间片,执行程序)
+**运行态**(获取到了时间片,执行程序)
-阻塞态(运行过程中等待获取其他资源,I/O请求等)
+**阻塞态**(运行过程中等待获取其他资源,I/O请求等)
-终止态(进程被杀死了)
+**终止态**(进程被杀死了)
#### 并发性
@@ -204,7 +205,7 @@ class ThreadTarget implements Runnable {
##### 原理
-之所以有这种实现方法,是因为Thread类的run方法中会判断成员变量target是否为空,不为空就会调用target类的run方法。
+之所以有这种实现方法,是因为Thread类的run()方法中会判断成员变量target是否为空,不为空就会调用target类的run方法。
```java
private Runnable target;
@@ -258,7 +259,7 @@ thread.start()
#### 第三种 实现Callable接口
-Runnable接口中的run方法是没有返回值,如果我们需要执行的任务带返回值就不能使用Runnable接口。创建一个类CallableTarget,实现Callable接口,实现带有**返回值的call()方法**,然后根据CallableTarget创建一个任务FutureTask,然后根据FutureTask来创建一个线程Thread,调用Thread的start方法可以执行任务。
+Runnable接口中的run()方法是没有返回值,如果我们需要执行的任务带返回值就不能使用Runnable接口。创建一个类CallableTarget,实现Callable接口,实现带有**返回值的call()方法**,然后根据CallableTarget创建一个任务FutureTask,然后根据FutureTask来创建一个线程Thread,调用Thread的start方法可以执行任务。
```java
public class CallableTarget implements Callable {
@@ -352,7 +353,7 @@ Callable跟Runnable类似,也是一个接口。只不过它的call方法有返
##### Future
-Future也是一个接口,Future就像是一个管理的容器一样,进一步对Runable和Callable的实例进行封装,提供了取消任务的cancel()方法,查询任务是否完成的isDone()方法,获取执行结果的get()方法,带有超时时间来获取执行结果的get()方法。
+Future也是一个接口,Future就像是一个管理的容器一样,进一步对Runable和Callable的实例进行封装,定义了一些方法。取消任务的cancel()方法,查询任务是否完成的isDone()方法,获取执行结果的get()方法,带有超时时间来获取执行结果的get()方法。
```java
public interface Future {
@@ -360,13 +361,9 @@ public interface Future {
//为true,如果任务已经执行,那么会调用Thread.interrupt()方法设置中断标识
//为false,如果任务已经执行,就只会将任务状态标记为取消,而不会去设置中断标识
boolean cancel(boolean mayInterruptIfRunning);
-
boolean isCancelled();
-
boolean isDone();
-
V get() throws InterruptedException, ExecutionException;
-
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
@@ -386,6 +383,40 @@ public interface RunnableFuture extends Runnable, Future {
}
```
+##### 使用案例
+
+使用时,Runnable实现类的实例可以作为Thread的入参使用,而Callable只能使用FutureTask进行封装使用。
+
+```java
+//Runnable配合Thread进行使用
+Thread threadA = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ //任务的代码
+ }
+ });
+
+//Callable使用FutureTask封装后,配合线程池进行使用
+ExecutorService pool = Executors.newSingleThreadExecutor();
+FutureTask task = new FutureTask(new Callable() {
+ @Override
+ public Object call() throws Exception {
+ //任务的代码
+ return null;
+ }
+});
+pool.submit(task);
+
+//Runnable使用FutureTask封装后,配合线程池进行使用
+FutureTask task1 = new FutureTask(new Runnable() {
+ @Override
+ public void run() {
+ //任务的代码
+ }
+});
+pool.submit(task1);
+```
+
### Java中单例有哪些写法?
正确并且可以做到延迟加载的写法其实就是三种:
@@ -762,14 +793,11 @@ static void ensure_join(JavaThread* thread) {
thread->clear_pending_exception();
- java_lang_Thread::set_thread_status(threadObj(), java_lang_Thread::TERMINATED);
-
+ java_lang_Thread::set_thread_status(threadObj(), java_lang_Thread::TERMINATED);
java_lang_Thread::set_thread(threadObj(), NULL);
-
//同志们看到了没,别的不用看,就看这一句
//thread就是当前线程,是啥?就是刚才例子中说的threadA线程
lock.notify_all(thread);
-
thread->clear_pending_exception();
}
```
@@ -837,7 +865,7 @@ FutureTask提供了cancel(boolean mayInterruptIfRunning)方法来取消任务,
##### 2.线程的stop方法
-线程的stop()方法可以让线程停止执行,但是不会释放当前线程持有的锁。但是这个方法已经被标记为过期,不推荐使用了。
+线程的stop()方法可以让线程停止执行,释放所有的锁,抛出ThreadDeath这种Error。但是在释放锁之后,没有办法让受这些锁保护的资源,对象处于一个安全,一致的状态。(例如有一个变量a,本来的值是0,你的线程任务是将a++后然后再进行a--。正常情况下任务执行完之后,其他线程取到这个变量a的值应该是0,但是如果之前调用了Thread.stop方法时,正好是在a++之后,那么变量a就会是1,这样其他线程取到的a就是出于不一致的状态。)
### 让线程顺序执行有哪些方法?
@@ -848,24 +876,24 @@ FutureTask提供了cancel(boolean mayInterruptIfRunning)方法来取消任务,
示例代码如下:
```java
- Thread threadA = new Thread(new Runnable() {
- @Override
- public void run() {
+Thread threadA = new Thread(new Runnable() {
+ @Override
+ public void run() {
//执行threadA的任务
- }
- });
- Thread threadB= new Thread(new Runnable() {
- @Override
- public void run() {
- //执行threadB的任务
- }
- });
- //执行线程A任务
- threadA.start();
- //主线程进行等待
- threadA.join();
- //执行线程B的任务
- threadB.start();
+ }
+});
+Thread threadB= new Thread(new Runnable() {
+ @Override
+ public void run() {
+ //执行threadB的任务
+ }
+});
+//执行线程A任务
+threadA.start();
+//主线程进行等待
+threadA.join();
+//执行线程B的任务
+threadB.start();
```
##### 子线程Join
@@ -982,7 +1010,7 @@ http://cnblogs.com/wenjunwei/p/10573289.html
### 线程间怎么通信?
-##### 1.synchronized锁
+#### 1.synchronized锁
通过synchronized锁来进行同步,让一次只能一个线程来执行。
@@ -1356,7 +1384,8 @@ private static class Producer {
```java
public class BlockQueueRepository extends AbstractRepository implements Repository {
- public BlockQueueRepository() {
+ public BlockQueueRepository(int cap) {
+ //cap代表队列的最大容量
products = new LinkedBlockingQueue<>(cap);
}
@@ -1422,9 +1451,13 @@ public ThreadPoolExecutor(int corePoolSize,
从阻塞队列取任务时,如果阻塞队列为空:
-核心线程的会一直卡在`workQueue.take`方法,被阻塞并挂起,不会占用CPU资源。
+**核心线程**的会一直卡在`workQueue.take`方法,这个take方法每种等待队列的实现各不相同,以LinkedBlockingQueue为例,
+
+在这个方法里会调用notEmpty队列(这是Condition实例)的await()方法一直阻塞并挂起,不会占用CPU资源。
-非核心线程会调用workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS)方法取任务 ,如果超过keepAliveTime时间后还没有拿到,下一次循环判断**compareAndDecrementWorkerCount**就会返回`null`,Worker对象的`run()`方法循环体的判断为`null`,任务结束,然后线程被系统回收)
+**非核心线程**会调用workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS)方法取任务 ,这个poll方法每种等待队列的实现各不相同,以LinkedBlockingQueue为例,
+
+在这个方法里面会调用notEmpty队列(这是Condition实例)的awaitNanos()方法进行超时等待,如果超过keepAliveTime时间后还没有新的任务,就会返回`null`,Worker对象的`run()`方法循环体的判断为`null`,任务结束,然后线程被系统回收)
##### 2.maximumPoolSize 最大线程数
@@ -1611,15 +1644,13 @@ ThreadPoolExecutor提供了如下几个public的setter方法

-
-
调用corePoolSize方法之后,线程池会直接覆盖原来的corePoolSize值,并且基于当前值和原始值的比较结果采取不同的处理策略。(总得来说就是,多退少补的策略)
**对于新corePoolSize<当前工作线程数的情况:**
说明有多余的worker线程,此时会向当前idle状态的worker线程发起中断请求以实现回收,多余的worker在下次idel的时候也会被回收。
-**对于新corePoolSize>当前工作线程数的情况:**
+**对于新corePoolSize>当前工作线程数且队列中有任务的情况:**
如果当前队列中有待执行任务,则线程池会创建新的worker线程来执行队列任务。
@@ -1707,3 +1738,103 @@ ThreadLocal变量所在的类的实例(代码中A的实例)->ThreadLocal
[《一篇文章,从源码深入详解ThreadLocal内存泄漏问题》](https://www.jianshu.com/p/dde92ec37bd1)
+### Random类取随机数的原理是什么?
+
+首先在初始化Random实例的时候就会根据当前的时间戳生成一个种子数seed。
+
+```java
+public Random() {
+ this(seedUniquifier() ^ System.nanoTime());
+ }
+
+ private static long seedUniquifier() {
+ // L'Ecuyer, "Tables of Linear Congruential Generators of
+ // Different Sizes and Good Lattice Structure", 1999
+ for (;;) {
+ long current = seedUniquifier.get();
+ long next = current * 181783497276652981L;
+ if (seedUniquifier.compareAndSet(current, next))
+ return next;
+ }
+ }
+```
+
+然后每次取随机数时是拿seed乘以一个固定值multiplier,作为随机数。
+
+```java
+ protected int next(int bits) {
+ long oldseed, nextseed;
+ AtomicLong seed = this.seed;
+ do {
+ oldseed = seed.get();
+ nextseed = (oldseed * multiplier + addend) & mask;
+ } while (!seed.compareAndSet(oldseed, nextseed));
+ return (int)(nextseed >>> (48 - bits));
+ }
+```
+
+但是这样的话,多线程并发使用Math.random取随机数时,同一个时间点取到的随机数一样的概率会比较大。所以可以使用ThreadLocalRandom.current().nextInt()方法去取随机数。每个线程第一次调用ThreadLocalRandomd的current()方法时,会为这个线程生成一个线程独立的种子数seed,这样多线程并发读取随机数时,可以保证取到的随机数都是不一样的。
+
+```java
+public static ThreadLocalRandom current() {
+ //判断这个线程是否生成种子
+ if (UNSAFE.getInt(Thread.currentThread(), PROBE) == 0)
+ localInit();
+ return instance;
+ }
+
+//为这个线程生成一个种子seed,并且将种子seed,和线程已生成种子的标志 存储到Unsafe类中
+static final void localInit() {
+ int p = probeGenerator.addAndGet(PROBE_INCREMENT);
+ int probe = (p == 0) ? 1 : p; // skip 0
+ long seed = mix64(seeder.getAndAdd(SEEDER_INCREMENT));
+ Thread t = Thread.currentThread();
+ UNSAFE.putLong(t, SEED, seed);
+ UNSAFE.putInt(t, PROBE, probe);
+ }
+
+//获取随机数,根据当前种子计算随机数
+public int nextInt(int origin, int bound) {
+ if (origin >= bound)
+ throw new IllegalArgumentException(BadRange);
+ return internalNextInt(origin, bound);
+ }
+
+final int internalNextInt(int origin, int bound) {
+ int r = mix32(nextSeed());
+ if (origin < bound) {
+ int n = bound - origin, m = n - 1;
+ if ((n & m) == 0)
+ r = (r & m) + origin;
+ else if (n > 0) {
+ for (int u = r >>> 1;
+ u + m - (r = u % n) < 0;
+ u = mix32(nextSeed()) >>> 1)
+ ;
+ r += origin;
+ }
+ else {
+ while (r < origin || r >= bound)
+ r = mix32(nextSeed());
+ }
+ }
+ return r;
+ }
+```
+
+### 僵尸进程,孤儿进程,守护进程是什么?
+
+僵尸进程:通常来说,使用fork()系统调用从一个父进程创建出一个子进程,子进程退出,是需要父进程调用wait()或者是waitpid()函数来回收子进程的资源,如果父进程没有调用,子进程的信息就会一直在内存中,而不会被回收,变成僵尸进程。
+
+孤儿进程:就是父进程先退出了,它的子进程会被init进程接管,由它来收集子进程的状态。(init进程是内核启动时,创建出来的进程,是一个以root身份运行的普通用户进程,是永远不会停止的。)
+
+守护进程是脱离于终端并且在后台运行的进程,脱离终端是为了避免将在执行的过程中的信息打印在终端上,并且进程也不会被任何终端所产生的终端信息所打断。
+
+### BlockingQueue的原理是怎么样的?
+
+https://www.cnblogs.com/tjudzj/p/4454490.html
+
+### 进程间通信的方式
+
+https://network.51cto.com/art/201911/606827.htm?mobile
+
diff --git a/docs/Kafka.md b/docs/Kafka.md
index 845c163..f3a4529 100644
--- a/docs/Kafka.md
+++ b/docs/Kafka.md
@@ -82,6 +82,8 @@
- 前一条消息发送失败,后一条消息发送成功,前一条消息重试后成功,造成数据乱序。
+ http://www.jasongj.com/kafka/transaction/
+
##### 消费端幂等性
只能自己从业务层面保证重复消费的幂等性,例如引入版本号机制。
@@ -136,3 +138,116 @@
ISR(in-sync replica) 就是 Kafka 为某个分区维护的一组同步集合,即每个分区都有自己的一个 ISR 集合,就是从分区的从节点中找出一些节点加入到ISR集合(min.insync.replicas这个参数设定ISR中的最小副本数是多少,默认值为1)。处于 ISR 集合中的副本,意味着 follower 副本与 leader 副本保持同步状态,只有处于 ISR 集合中的副本才有资格被选举为 leader。follower从leader同步数据有一些延迟(延迟时间replica.lag.time.max.ms),一旦超过延迟时间,就会把这个这个follower从ISR列表中移除。被移除的followe会从leader复制数据进行追赶,一旦追赶上又可以重新进入ISR列表。一条 Kafka 消息,只有被 ISR 中的副本都接收到,才被视为“已同步”状态。这跟 zk 的同步机制不一样,zk 只需要超过半数节点写入,就可被视为已写入成功。
+### 什么是零拷贝技术?
+传统的IO接口像read和write系统调用,在执行过程中都是涉及到数据拷贝操作的,比如调用read()接口去读取一个文件时,首先需要将CPU由用户切换成内核态,然后把文件从磁盘读取到
+
+#### read()和write()
+
+
+
+**read()系统调用的步骤:**
+1.会涉及到到一次用户态到内核态的切换,然后会发出 sys_read()系统调用,从文件读取数据。(一次上下文切换)
+2.磁盘控制器会使用DMA技术将磁盘文件拷贝到内核内存空间的缓冲区。(一次DMA拷贝)
+3.CPU会将数据从内核内存空间的缓冲区拷贝到用户进程内存空间的缓冲区。(一次CPU拷贝)
+4.然后read()系统调用返回后,会进行内核态往用户态的切换,这样用户程序进程就可以修改数据了。(一次上下文切换)
+
+**write()系统调用的步骤:**
+1.首先会涉及CPU从用户态切换到内核态,然后会将数据从用户程序的内存空间拷贝到内核内存空间中的Socket缓冲区。(一次上下文切换,一次CPU拷贝)
+2.网卡会使用DMA技术,将数据从内核内存空间中的缓冲区拷贝到网卡。(一次DMA拷贝)
+3.write()调用完成后会从内核态切换到用户态。(一次上下文切换)
+
+#### 2.MMAP和write()
+
+
+
+##### mmap
+
+1.CPU从用户态切换到内核态,磁盘控制器使用DMA技术将数据从磁盘拷贝到内核的内存空间。不会将数据拷贝到用户程序的内存空间,而是将一块物理内存让用户进程的空间与内核空间进行共享,将内核中的这部分内存空间映射到用户进程的内存空间,从而让用户进程可以直接访问这部分内存。(一次上下文切换,一次DMA拷贝)
+
+2.mmap调用完毕后,CPU会从内核态切换到用户态。(一次上下文切换)
+
+mmap相比于read()系统调用还是会有2次上下文切换,但是可以减少一次CPU拷贝,因为数据是存在内核的内存空间中。
+
+##### write
+
+1.首先CPU从用户态切换到内核态,然后把数据从内核的内存空间拷贝到内核中Socket缓冲区。(一次上下文切换,一次CPU拷贝)
+
+2.网卡使用DMA技术,将数据从Socket缓冲区拷贝到网卡。发送完毕后,从内核态切换为用户态。(一次上下文切换,一次DMA拷贝)
+
+https://mp.weixin.qq.com/s/xDZ9NnyUZSoR9npuMLdpWA
+https://blog.csdn.net/choumu8867/article/details/100658332
+
+#### sendfile
+
+这种方式只能用于发送文件,不能修改文件,在Kakfa发送消息给消费者时有用到。
+
+
+
+读取时:
+
+1.首先CPU从用户态切换成内核态,然后磁盘控制器使用DMA技术将文件从磁盘拷贝到内核空间的缓冲区中。
+
+(一次上下文切换,一次DMA拷贝)
+
+发送时:
+
+2.早期的版本是将数据从内核空间中的缓存区拷贝到内核空间的Socket缓冲区,在Linux 2.4以后,是只需要将数据在内核空间的文件数据缓存中的位置和偏移量写入到Socket缓存中,然后网卡直接从Socket缓存中读取文件的位置和偏移量,使用DMA技术拷贝到网卡。发送完毕后,从内核态切换为用户态。
+
+(一次上下文切换,一次DMA拷贝。)
+
+##### 总结:
+
+传统read()和write()方案:数据拷贝了4次,CPU上下文切换了很多次
+
+mmap和write()方案:数据拷贝了3次,会减少一次CPU拷贝,上下文切换了4次。(可以减少1次CPU拷贝)
+
+sendfile方案:数据拷贝了2次,上下文切换了2次。但是用户进程不能修改数据。(可以减少2次CPU拷贝,至少2次上下文切换)
+
+### Kafka刷盘时机是怎么样的?
+log.flush.interval.messages 最大刷盘消息数量
+log.flush.interval.interval.ms 最大刷盘时间间隔
+log.flush.scheduler.interval.ms 定期刷盘间隔
+可以通过设置 最大刷盘消息数量 和 最大刷盘时间间隔 来控制fsync系统调用的时间,但是Kafka不推荐去设置这些参数,希望让操作系统来决定刷盘的时机,这样可以支持更高的吞吐量。而且Kafka保证可用性是通过多副本来实现的,一个机器挂掉了就会选举副本作为leader。
+### Kafka什么时候进行rebalance?
+1.topic下分区的数量增加了或者减少了。(这个一般是我们手动触发的)
+
+2.消费者的数量发生了改变,例如新增加了消费者或者有消费者挂掉了。
+Kafka有一个session.timeout.ms,最大会话超时时间,最长是10s。就是如果broker与消费者之间的心跳包超过10s还没有收到回应,就会认为消费者掉线了。以及还有一个max.poll.interval.ms参数,消费者两次去broker拉取消息的间隔,默认是5分钟。如果消费者两次拉取消息的间隔超过了5分钟,就会认为消费者掉线了。
+
+一旦发生rebalance了,有可能会导致重复消费的问题,就是消费者A拉取了100条消息,消费时间超过了5分钟,被broker认定下线,就会进行rebalance,把这个分区分配给其他消费者消费,其他消费者就会进行重复消费。
+
+怎么解决rebalance带来的重复消费问题呢?
+
+1.可以减少每批消息的处理时间,让每条消息的处理时间减少,或者是修改max.poll.records,减小每次拉取消息的数量。
+
+2.可以自行在MySQL或者Redis里面存储每个分区消费的offset,然后消费者去一个新的分区拉取消息时先去读取上次消费的offset。
+
+3.为消息分配一个唯一的消息id,通过消息id来判定是否重复消费了。
+
+##### kafka 1.1的优化
+
+新版本新增了**group.initial.rebalance.delay.ms**参数。空消费组接受到成员加入请求时,不立即转化到PreparingRebalance状态来开启reblance。当时间超过**group.initial.rebalance.delay.ms**后,再把group状态改为PreparingRebalance(开启reblance),这样可以避免服务启动时,consumer陆续加入引起的频繁Rebalance。
+
+##### Kafka2.3对reblance的优化
+
+但对于运行过程中,consumer超时或重启引起的reblance则无法避免,其中一个原因就是,consumer重启后,它的身份标识会变。简单说就是Kafka不确认新加入的consumer是否是之前挂掉的那个。
+
+在Kafka2.0中引入了静态成员ID,使得consumer重新加入时,可以保持旧的标识,这样Kafka就知道之前挂掉的consumer又恢复了,从而不需要Reblance。这样做的好处有两个:
+
+1. 降低了Kafka Reblance的频率
+2. 即使发生Reblance,Kafka尽量让其他consumer保持原有的partition,减少了重分配引来的耗时、幂等等问题
+
+https://blog.csdn.net/weixin_37968613/article/details/104607012
+
+https://blog.csdn.net/z69183787/article/details/105138782
+
+https://zhuanlan.zhihu.com/p/87577979
+
+https://www.cnblogs.com/runnerjack/p/12108132.html
+
+### kafka的选举机制
+
+https://blog.csdn.net/qq_37142346/article/details/91349100
+
+https://honeypps.com/mq/kafka-basic-knowledge-of-selection/
+
diff --git a/docs/LeetCode.md b/docs/LeetCode.md
index 323e52e..5b5c42a 100644
--- a/docs/LeetCode.md
+++ b/docs/LeetCode.md
@@ -4,6 +4,7 @@
##### [第1题-两数之和](#第1题-两数之和)
##### [第206题-反转链表](#第206题-反转链表)
+
##### [第2题-两数相加](#第2题-两数相加)
##### [第3题-无重复字符的最长子串](#第3题-无重复字符的最长子串)
##### [第20题-有效的括号](#第20题-有效的括号)
@@ -649,8 +650,15 @@ public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
### 第283题-移动零
+给定一个数组 `nums`,编写一个函数将所有 `0` 移动到数组的末尾,同时保持非零元素的相对顺序。
+
题目详情:https://leetcode-cn.com/problems/move-zeroes/
+```
+输入: [0,1,0,3,12]
+输出: [1,3,12,0,0]
+```
+
解题思路:
对数组进行遍历,就是找到一个为0的数,然后继续往后找,找到一个不为0的数,与它进行交换,这样就可以把0全部移动到后面去了。
@@ -763,7 +771,9 @@ int findLeftBound(int[] nums,double target) {
##### 解题思路
-我们取两个指针从数组的两端往内遍历,i从数组头部出发,j从数组尾部出发,对于两个端点i和j来说,容纳水的面积是是等于(j-i)*min(height[i],height[j]),假设height[i]是两者之间较小的那一个,那么面积等于(j-i)*height[i],假设i不移动,j向左移动,这样宽度j-i会减少,而height[j]即便变大也不会使得面积变大,因为面积是由宽度乘以两者中较小的高度决定的,所以此时的面积对于i这个端点来说,已经是最大的面积,我们可以右移端点i。
+我们取两个指针从数组的两端往内遍历,i从数组头部出发,j从数组尾部出发。每次计算最大面积,并且移动高度较小的那个端点。
+
+对于两个端点i和j来说,容纳水的面积是是等于(j-i)*min(height[i],height[j]),假设height[i]是两者之间较小的那一个,那么面积等于(j-i)*height[i],假设i不移动,j向左移动,这样宽度j-i会减少,而height[j]即便变大也不会使得面积变大,因为面积是由宽度乘以两者中较小的高度决定的,所以此时的面积对于i这个端点来说,已经是最大的面积,我们可以右移高度较小的端点i。
```java
public int maxArea(int[] height) {
@@ -1019,7 +1029,6 @@ public int maxDepth(TreeNode root) {
public void generateParenthesis(int n,int left,int right,LinkedList stack,List totalList) {
//不满足要求,进行剪枝,回退去遍历其他节点
-
if(left>n || right>n || right>left) {return;}
if(left==n&&right==n) {//正好匹配上了,将栈中所有值转换为
StringBuffer str = new StringBuffer();
@@ -1046,6 +1055,8 @@ PS:回朔算法的框架
```
List result = new ArrayList<>();
void backtrack(路径, 选择列表stack):
+ if 超出范围 return
+
if 满足结束条件:
result.add(路径)
return
@@ -1180,6 +1191,8 @@ f(0)=nums[0];
f(1)=nums[1]>nums[0]?nums[1]:nums[0];//也就是num[0]和nums[1]之间的最大值。
+状态转移方程如下:
+
f(n) = f(n-1) > f(n-2)+nums[n] ? f(n-1) :f(n-2)+nums[n]
```java
@@ -1387,7 +1400,9 @@ public boolean canJump(int[] nums) {
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。
-问总共有多少条不同的路径?
+问总共有多少条不同的路径? 右6 下2 C 2 8
+
+

@@ -1695,7 +1710,6 @@ public ListNode mergeKLists(ListNode[] lists) {
}
}
}
-
void swap(ArrayList lists,int a, int b) {
ListNode temp = lists.get(a);
lists.set(a,lists.get(b));
@@ -2106,6 +2120,7 @@ public List> subsets(int[] nums) {
for (int i = 0; i < size; i++) {
List list = new ArrayList<>();
for (int j = 0; j < nums.length; j++) {
+ //j是元素在数组中的位置,通过判断i的第j个二进制位是否为0,来决定是否添加这个元素
int result = i & (1<0 ; i--) {
- if (nums[i-1] nums[i-1]) {
min = j;
}
}
+ //进行交换
int temp = nums[i-1];
nums[i-1] = nums[min];
nums[min] = temp;
flag=1;
+ //然后再对后面的元素进行排序
Arrays.sort(nums,i,nums.length);
break;
}
}
+ //如果现在的数组就是字典序最大的,那么就排序,得到字典序最小的进行返回。
if (flag==0) {
Arrays.sort(nums);
}
@@ -2456,3 +2475,40 @@ dp[i] = max(1,dp[k]+1); ksizeArray[maxSize]) {//如果当前元素比最长子序列的尾部元素大
+ maxSize++;
+ sizeArray[maxSize] = nums[i];
+ } else if (nums[i]==sizeArray[maxSize]) {//等于尾部元素,那么不用更新
+ continue;
+ } else {//小于尾部元素进行更新,在sizeArray中进行二分查找
+ int left = 0;
+ int right = maxSize;
+ while (left < right) {
+ int mid = (left+right)/2;
+ if (sizeArray[mid] < nums[i]) {
+ left = mid+1;
+ } else {
+ right = mid;
+ }
+ }
+ //最后left的位置肯定就是需要进行插入的位置
+ //left左边的元素都比nums[i]小
+ sizeArray[left]=nums[i];
+ }
+ }
+ return maxSize+1;
+}
+```
\ No newline at end of file
diff --git a/docs/LeetCode1.md b/docs/LeetCode1.md
index 075536f..bb25d1a 100644
--- a/docs/LeetCode1.md
+++ b/docs/LeetCode1.md
@@ -4,9 +4,12 @@
##### [第155题-最小栈](#第155题-最小栈)
##### [第160题-相交链表](#第160题-相交链表)
+
##### [第142题-环形链表II](#第142题-环形链表II)
##### [第739题-每日温度](#第739题-每日温度)
+
##### [第347题-前K个高频元素](#第347题-前K个高频元素)
+
##### [第49题-字母异位词分组](#第49题-字母异位词分组)
##### [第32题-最长有效括号](#第32题-最长有效括号)
##### [第543题-二叉树的直径](#第543题-二叉树的直径)
@@ -204,7 +207,7 @@ public ListNode detectCycle(ListNode head) {
quick = quick.next;
quick = quick.next;
}
- //计算环的长度
+ //计算环的长度
int circleLength = 1;
ListNode copySlow = slow.next;
while (copySlow != slow) {
@@ -435,23 +438,21 @@ public int[] topKFrequent(int[] nums, int k) {
##### 解题思路
其实根节点的直径就是左节点深度+右节点深度,其他节点也是这个公示,所以可以递归求解出左右节点的深度,然后计算当前节点的直径,然后与左右节点的直径进行判断,返回最大值。
```java
- public int diameterOfBinaryTree(TreeNode root) {
- if (root==null){return 0;}
- int leftDepth = maxDepth(root.left);
- int rightDepth = maxDepth(root.right);
- int rootDiameter = leftDepth+rightDepth+1;
- int leftDiameter = diameterOfBinaryTree(root.left);
- int rightDiameter = diameterOfBinaryTree(root.right);
- int maxDiameter = rootDiameter > leftDiameter ? rootDiameter : leftDiameter;
- maxDiameter = maxDiameter > rightDiameter ? maxDiameter:rightDiameter;
- return maxDiameter;
- }
- public int maxDepth(TreeNode root) {
- if (root==null) {return 0;}
- int leftDepth = maxDepth(root.left);
- int rightDepth = maxDepth(root.right);
- return leftDepth>rightDepth?leftDepth+1 : rightDepth+1;
- }
+int maxDiameter = 0;
+public int diameterOfBinaryTree(TreeNode root) {
+ if (root==null){return 0;}
+ maxDepth(root);
+ return maxDiameter;
+}
+
+public int maxDepth(TreeNode root) {
+ if (root==null) {return 0;}
+ int leftDepth = maxDepth(root.left);
+ int rightDepth = maxDepth(root.right);
+ int diameter = leftDepth + rightDepth;
+ maxDiameter = diameter > maxDiameter ? diameter : maxDiameter;
+ return leftDepth>rightDepth?leftDepth+1 : rightDepth+1;
+}
```
### 第79题-单词搜索
@@ -459,7 +460,7 @@ public int[] topKFrequent(int[] nums, int k) {
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
-
+
示例:
@@ -574,7 +575,31 @@ public int numTrees(int n) {
}
```
+递归解法
+
+```java
+int[] cacheArray;
+public int numTrees(int n) {
+ if(n == 0 || n == 1){return 1;}
+ if(n==2) {
+ return 2;
+ }
+ if(cacheArray == null) {
+ cacheArray = new int[n+1];
+ } else if (cacheArray[n] !=0) {
+ return cacheArray[n];
+ }
+ int sum = 0;
+ for(int i = 0; i map = new LinkedHashMap();
- int capacity;
+int capacity;
- public LRUCache(int capacity) {
- this.capacity = capacity;
- }
+public LRUCache(int capacity) {
+ this.capacity = capacity;
+}
- public int get(int key) {
- Integer value = map.get(key);
- if (value == null) {
- return -1;
- }
- map.remove(key);
- map.put(key,value);
- return value;
- }
-
- public void put(int key, int value) {
- Integer oldValue = map.get(key);
- if (oldValue!=null) {
- //只是覆盖value的话,put方法不会改变键值对在链表中的顺序,所以需要先remove
- map.remove(key);
- map.put(key,value);
- return;
- }
- if (map.size()>=capacity) {
- map.remove(map.keySet().iterator().next());
- }
- map.put(key,value);
- }
+public int get(int key) {
+ Integer value = map.get(key);
+ if (value == null) {
+ return -1;
+ }
+ map.remove(key);
+ map.put(key,value);
+ return value;
+}
+
+public void put(int key, int value) {
+ Integer oldValue = map.get(key);
+ if (oldValue!=null) {
+ //只是覆盖value的话,put方法不会改变键值对在链表中的顺序,所以需要先remove
+ map.remove(key);
+ map.put(key,value);
+ return;
+ }
+ if (map.size()>=capacity) {
+ map.remove(map.keySet().iterator().next());
+ }
+ map.put(key,value);
+}
```
### 第236题-二叉树的最近公共祖先
@@ -693,12 +718,13 @@ LinkedHashMap map = new LinkedHashMap();
3.节点1,节点2全部位于此节点的左子树,或者是右子树
4.此节点左子树包含节点1,右子树包含节点2
所以第4种就是我们要寻找的节点,并且在二叉树中只有一个,所以我们对二叉树进行遍历,判断某个节点左子树,右子树都包含节点,那么就返回该节点。
+
```java
TreeNode lowestCommonAncestor(TreeNode root, TreeNode node1, TreeNode node2) {
if (root==null) {
return null;
}
- if (root==node1 || root==node2) {//当前节点就是要找的节点之一
+ if (root==node1 || root==node2) {//当前节点就是要找的节点之一
return root;
}
TreeNode leftNode = lowestCommonAncestor(root.left,node1,node2);//判断左子树中是否有节点
@@ -793,11 +819,19 @@ TreeNode lowestCommonAncestor(TreeNode root, TreeNode node1, TreeNode node2) {
矩形面积=height[i]*(right-left+1),例如对于第五个柱子值为2的那个柱子来说,左边界left就是值为5的柱子,右边界就是值为3的柱子。
-也可以使用一个最小栈Stack来保存过程中比height[i]小的元素,也就是Stack中的值只能是比height[i]小的元素。
+单调栈解法
-遍历时,如果现在的height[i]比栈顶的元素height[j]小,那么就进行出栈,并且对出栈的元素计算矩形面积,因为出栈的元素的left边界就是新栈顶的index+1,right边界就是现在的i-1。
+理论就是假设f(i)代表包含圆柱i的最大矩形,那么其实就是在圆柱i的左边和右边各自找到第一个高度更小的圆柱k和j,f(i) = height[i]*(j-k+1)。所以可以使用单调栈来保存比当前圆柱高度height[i]小的元素,如果栈中元素高度比height[i]大,那么那些元素就需要出栈了。
- 面积=heights[j] * (right - left+1)
+
+
+可以使用一个栈Stack来保存左边圆柱高度比height[i]小的元素,也就是Stack中的值只能是比height[i]小的元素。栈底元素是最小的,栈顶元素高度是最大的。
+
+遍历时,如果当前height[i]>栈顶元素的高度,说明当前栈顶元素还没有碰到比它小的数,所以还不能计算面积,就把height[i]入栈。
+
+如果现在的height[i]<栈顶的元素高度小,说明栈顶元素碰到比它小的元素了,就需要出栈计算矩形面积,
+
+ 面积=heights[j] * (i - left)
```java
public int largestRectangleArea(int[] heights) {
@@ -827,13 +861,12 @@ public int largestRectangleArea(int[] heights) {
Integer index = stack.pop();
//此时area的范围是[stack.peek()+1,i-1]
int leftIndex;
- int rightIndex = i-1;
if (stack.size()>0) {
leftIndex = stack.peek() + 1;
} else {
leftIndex = 0;
}
- int area = heights[index] * (rightIndex - leftIndex+1);
+ int area = heights[index] * (i - leftIndex);
maxArea = maxArea > area ? maxArea : area;
}
stack.push(i);
@@ -1034,7 +1067,6 @@ public int maxProduct(int[] nums) {
if (nums[i] < 0 && firstNegativeIndex == null) {
firstNegativeIndex = i;
}
-
if (nums[i] < 0) {
lastNegativeIndex = i;
}
@@ -1325,7 +1357,7 @@ public String minWindow(String s, String t) {
两个整数之间的汉明距离指的是这两个数字对应二进制位不同的位置的数目。
给出两个整数 x 和 y,计算它们之间的汉明距离。
注意:
-0 ≤ x, y < 231.
+0 ≤ x, y < 2的31次方.
示例:
@@ -1346,13 +1378,13 @@ public int hammingDistance(int x, int y) {
int result = x^y;
int num=0;
while(result>0) {
- if((result&1) == 1) {
- num++;
- }
- result = result>>1;
+ if((result&1) == 1) {
+ num++;
+ }
+ result = result>>1;
}
return num;
- }
+}
```
### 第128题-最长连续序列
@@ -1549,10 +1581,15 @@ public int longestConsecutive(int[] nums) {
其实就是判断有向图是否存在环,有两种解法
##### 深度优先遍历
就是先根据二维数组构建一个邻接表,这里我们使用一个map来作为领接表,然后递归遍历map中的节点,对于图中每个节点有三种状态:
-1.未被访问过(在statusMap中值为null)
-2.已被访问过,并且它的子节点没有遍历访问完成(在statusMap中值为1)
-3.已被访问过,并且子节点也遍历访问完成(在statusMap中值为2)
+
+1.未被访问过(在statusMap中值为null)。
+
+2.已被访问过,并且它的子节点没有遍历访问完成(在statusMap中值为1)。
+
+3.已被访问过,并且子节点也遍历访问完成(在statusMap中值为2)。
+
在递归遍历过程中,遇到上面第2种节点,说明就存在环。
+
```java
public boolean canFinish(int numCourses, int[][] prerequisites) {
HashMap> map = new HashMap<>();
@@ -1600,7 +1637,7 @@ public boolean canFinish(int numCourses, int[][] prerequisites) {
}
```
##### 拓扑排序
-这种解法有点像是宽度优先遍历,就是先建立邻接表,并且计算每个节点的入度,然后找到入度为0的节点(也就是没有被其他节点指向的节点),将它们入队列,然后对队列元素进行出队操作,取出队首元素,将它的子节点的入度都-1,然后子节点入度减到0时,就将这个子节点添加到队列中,在过程中会统计入过队列的节点数。原理就是如果没有环的,最终队列出队完成后,进入过队列的节点数是等于总节点数的。就是假设图的结构是1->2,2->3,3->4,4->2,也就是2,3,4形成一个环,最开始1是入度为0的节点,1会入队列,然后对节点2的入度-1,节点2的入度还剩下1,此时2不会入队列,所以最终进过队列的元素只有节点1,所以最终统计的数量是<总节点数的。
+这种解法有点像是宽度优先遍历,就是先建立邻接表,并且计算每个节点的入度,然后找到入度为0的节点(也就是没有被其他节点指向的节点),将它们入队列,然后对队列元素进行出队操作,取出队首元素,将它的子节点的入度都-1,然后子节点入度减到0时,就将这个子节点添加到队列中,在过程中会统计入过队列的节点数。原理就是如果没有环的,最终队列出队完成后,进入过队列的节点数是等于总节点数的。就是假设图的结构是1->2,2->3,3->4,4->2,也就是2,3,4形成一个环,最开始1是入度为0的节点,1会入队列,然后对节点2的入度-1,节点2的入度还剩下1,此时2不会入队列,所以最终进过队列的元素只有节点1,所以最终统计的数量是<总节点数的(如果不存在环,则所有节点的入度都会变成0,也就是结果集中的节点树会等于总节点数)。
### 第309题-最佳买卖股票时机含冷冻期
@@ -1610,6 +1647,21 @@ public boolean canFinish(int numCourses, int[][] prerequisites) {
你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。
+
+``` java
+dp[i][0]//代表不持有股票
+dp[i][1]//代表持有股票
+
+ //如果今天不持有股票,要么是之前没有股票,要么是卖了股票
+ dp[i][0] = max(dp[i-1][0],dp[i-1][1] + value[i])
+ //如果今天持有股票,要么是之前就持有,要么是今天新买的
+ dp[i][1] = max(dp[i-1][1],dp[i-2][0] - value[i])
+
+
+```
+
+
+
示例:
输入: [1,2,3,0,2]
@@ -1617,10 +1669,19 @@ public boolean canFinish(int numCourses, int[][] prerequisites) {
解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]
##### 解题思路
+
这个跟上一题的区别就是有冷冻期,就是当你第i天要持有股票时,要么是第i-1天已持有股票,要么是第i-1天没有买卖股票才能在第天买股票。
卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。
-第i天不持有股票 dp[i][0] = max(dp[i-1][1]+prices[i], dp[i-1][0])
-第i天持有股票 dp[i][1] = max(dp[i-2][0]-prices[i],dp[i-1][1])
+
+```java
+ //如果今天不持有股票,要么是之前没有股票,要么是第i天卖了股票
+ dp[i][0] = max(dp[i-1][0],dp[i-1][1] + value[i])
+ //如果今天持有股票,要么是之前就持有,要么是第i天新买的
+ dp[i][1] = max(dp[i-1][1],dp[i-2][0] - value[i])
+```
+
+代码
+
```java
public int maxProfit(int[] prices) {
if(prices==null||prices.length<=1) {return 0;}
@@ -1680,7 +1741,6 @@ public boolean canPartition(int[] nums) {
return canPartition(nums,nums.length-1,sum/2);
}
}
-
//使用HashMap缓存结果,避免重复计算
HashMap resultCacheMap = new HashMap();
//判断在0到i-1这些元素中进行能选择,看能否选择出的元素和为sum
@@ -1795,13 +1855,13 @@ public List findDisappearedNumbers(int[] nums) {
root = [10,5,-3,3,2,null,11,3,-2,null,1], sum = 8
- 10
- / \
- 5 -3
- / \ \
- 3 2 11
- / \ \
-3 -2 1
+ 10
+ / \
+ 5 -3
+ / \ \
+ 3 2 11
+ / \ \
+3 -2 1
返回 3。和等于 8 的路径有:
@@ -1819,8 +1879,8 @@ int pathNum = 0;
//必须包含根节点的
pathSumMustHasRoot(root,sum,0);
//不包含根节点的
- pathSum(root.left,sum);
- pathSum(root.right,sum);
+ pathSumMustHasRoot(root.left,root.val);
+ pathSumMustHasRoot(root.right,root.val);
return pathNum;
}
//对每个节点计算路径和,然后继续向下
@@ -1947,9 +2007,10 @@ public int[] countBits(int num) {
你可以将以下二叉树:
- 1
- / \
- 2 3
+ 1
+
+ / \
+ 2 3
/ \
4 5
@@ -2080,10 +2141,10 @@ public List findAnagrams(String s, String p) {
if (valid_num==p.length()) {//说明满足需求
list.add(left);
}
- //窗口左移
+ //窗口左边界移动一个字符
Character leftChar = s.charAt(left);
if (needMap.containsKey(leftChar)) {
- //移除左边界字符
+ //移除一个左边界字符
Integer times = windowsMap.get(leftChar);
times--;
windowsMap.put(leftChar,times);
@@ -2156,7 +2217,7 @@ public boolean searchMatrix(int[][] matrix, int target) {
给定一个非负整数数组,a1, a2, ..., an, 和一个目标数,S。现在你有两个符号 + 和 -。对于数组中的任意一个整数,你都可以从 + 或 -中选择一个符号添加在前面。
返回可以使最终数组和为目标数 S 的所有添加符号的方法数。
-示例:
+示例:
输入:nums: [1, 1, 1, 1, 1], S: 3
输出:5
@@ -2281,7 +2342,7 @@ public int findUnsortedSubarray(int[] nums) {
if (nums==null||nums.length==0) {
return 0;
}
- //从左开始遍历,找到需要调整的最右边的位置的下标
+ //从左往右开始遍历,记录最大值,找到需要调整的最右边的位置的下标
Integer right = null;
int maxIndex = 0;
for (int i = 0; i < nums.length; i++) {
@@ -2290,7 +2351,7 @@ public int findUnsortedSubarray(int[] nums) {
}
maxIndex = nums[i] > nums[maxIndex] ? i : maxIndex;
}
- //从右开始遍历,找到需要调整的最右边的位置的下标
+ //从右往左开始遍历,记录最小值,找到需要调整的最右边的位置的下标
Integer left = null;
int minIndex = nums.length-1;
for (int i = nums.length-1; i >=0; i--) {
diff --git a/docs/Lock.md b/docs/Lock.md
index 0a19772..1f1fb21 100644
--- a/docs/Lock.md
+++ b/docs/Lock.md
@@ -76,7 +76,7 @@ public class SyncTest {
就是Java对象的内存布局其实由对象头+实例数据+对齐填充三部分组成,而对象头主要包含Mark Word+指向对象所属的类的指针组成。Mark Word主要用于存储对象自身的运行时数据,哈希码,GC分代年龄,锁标志等。
-
+
下面就是Mark Word的数据映射表
@@ -202,7 +202,7 @@ JDK采用了适应性自旋,简单来说就是线程如果自旋成功了,
### AbstractQueuedSynchronizer(缩写为AQS)是什么?
AQS就是AbstractQueuedSynchronizer,抽象队列同步器,是一个可以用于实现基于先进先出等待队列的锁和同步器的框架。实现锁
-ReentrantLock,Semaphore,ReentrantReadWriteLock,SynchronousQueue,FutureTask等等皆是基于AQS的。
+ReentrantLock,CountDownLatch,Semaphore,ReentrantReadWriteLock,SynchronousQueue,FutureTask等等皆是基于AQS的。
ReentrantLock其实就是有一个变量sync,Sync父类是AbstractQueuedSynchronizer
@@ -222,30 +222,38 @@ ReentrantLock的非公平锁与公平锁的区别在于非公平锁在CAS更新s
### synchronized锁与ReentrantLock锁的区别?
-相同点:
+**相同点:**
1.可重入性
两个锁都是可重入的,持有锁的线程再次申请锁时,会对锁的计数器+1。
-不同点:
+**不同点:**
-1.具体实现
+**1.实现原理**
-synchronized是一个Java 关键字,synchronized锁是JVM实现的,底层代码应该是C++代码,而ReenTrantLock是JDK实现的,是Java提供的一个类库,代码是Java代码,源码实现更加方便阅读。
+synchronized是一个Java 关键字,是由JVM实现的,底层代码应该是C++代码。而ReentrantLock是JDK实现的,是Java提供的一个类库,代码是Java代码,源码实现更加方便阅读。
-2.性能
+**2.性能**
-在以前,synchronized锁的实现只有重量级锁一种模式,性能会比较差,后面引入了偏向锁和轻量级锁后就优化了很多。
+在以前,synchronized锁的实现只有重量级锁一种模式,性能会比较差,后面引入了偏向锁和轻量级锁后就优化了很多。根据测试结果,在线程竞争不激烈的情况下,ReentrantLock与synchronized锁持平,竞争比较激烈的情况下,ReentrantLock会效率更高一些。
-3.功能
+**3.功能**
-synchronized只能修饰方法,或者用于代码块,而ReenTrantLock的加锁和解锁是调用lock和unlock方法,更加灵活。其次是synchronized的等待队列只有一个(调用wait()方法的线程会进入等待队列),而ReenTrantLock可以有多个条件等待队列。可以分组唤醒需要唤醒的线程们,而不是像synchronized要么用notify方法随机唤醒一个线程要么用notifyAll方法唤醒全部线程。ReenTrantLock 提供了一种能够中断等待锁的线程的机制,就是线程通过调用lock.lockInterruptibly()方法来加锁时,一旦线程被中断,就会停止等待。
+synchronized只能修饰方法,或者用于代码块,而ReentrantLock的加锁和解锁是调用lock和unlock方法,更加灵活。
-4.公平性
+其次是synchronized的等待队列只有一个(调用wait()方法的线程会进入等待队列),而ReentrantLock可以有多个条件等待队列。可以分组唤醒需要唤醒的线程们,而不是像synchronized要么用notify方法随机唤醒一个线程要么用notifyAll方法唤醒全部线程。ReentrantLock 提供了一种能够中断等待锁的线程的机制,就是线程通过调用lock.lockInterruptibly()方法来加锁时,一旦线程被中断,就会停止等待。
+
+ReentrantLock可以使用tryLock(long timeout, TimeUnit unit)方法来尝试申请锁,设置一个超时时间,超过超时时间,就会直接返回false,而不是一直等待锁。
+
+ReentrantLock可以响应中断,而synchronized锁不行
+
+**4.公平性**
synchronized锁是非公平锁,ReentrantLock有公平锁和非公平锁两种模式。
+https://www.codercto.com/a/22884.html
+
### ReentrantLock的加锁流程是怎么样的?
ReentrantLock非公平锁的加锁流程:
@@ -272,9 +280,15 @@ ReentrantLock非公平锁的加锁流程:
https://blog.csdn.net/qq_14996421/article/details/102967314
-##### 谈一谈你对AQS的理解?
+https://blog.csdn.net/fuyuwei2015/article/details/83719444
+
+### 谈一谈你对AQS的理解?
+
+AQS是AbstractQueuedSynchronizer的缩写,是一个抽象同步队列类,可以基于它实现一个锁,例如ReentrantLock,只是需要实现tryAcquire()方法(也就是获取资源的方法,判断当前线程能否申请到独占锁)和tryRelease()方法(也就是释放资源的方法,在线程释放锁前对state进行更新),AQS会根据tryAcquire()的返回结果,来进行下一步的操作,
+
+如果为true,代表线程获得锁了。
-AQS是AbstractQueuedSynchronizer的缩写,是一个抽象同步队列类,可以基于它实现一个锁,例如ReentrantLock,只是需要实现tryAcquire()方法(也就是获取资源的方法,判断当前线程能否申请到独占锁)和tryRelease()方法(也就是释放资源的方法,在线程释放锁前对state进行更新),AQS会根据tryAcquire()的返回结果,来进行下一步的操作,如果为true,代表线程获得锁了,如果为false,代表线程没有获得锁,由AQS负责将线程添加到CLH等待队列中,并且进行阻塞等待。当前一个线程释放锁时,AQS对这个线程进行唤醒。
+如果为false,代表线程没有获得锁,由AQS负责将线程添加到CLH等待队列中,并且进行阻塞等待。当前一个线程释放锁时,AQS对这个线程进行唤醒。
(不同的自定义同步器争用共享资源的方式也不同。**自定义同步器在实现时只需要实现资源state的获取与释放方法即可**,至于具体线程等待队列的维护(如获取资源失败入队/唤醒出队等),AQS已经在顶层实现好了)
@@ -301,7 +315,7 @@ AbstractQueuedSynchronizer.acquireQueued()//并且让这个线程进入阻塞等
-可以看到AQS的acquire()方法中是会先去调用tryAcquire()去尝试着申请独占锁资源,AQS默认的tryAcquire9)方法只有一行代码,会抛出UnsupportedOperationException异常,所以ReentrantLock的FairSync对tryAcquire()方法进行了实现。
+可以看到AQS的acquire()方法中是会先去调用tryAcquire()去尝试着申请独占锁资源,AQS默认的tryAcquire()方法只有一行代码,会抛出UnsupportedOperationException异常(强制子类对这个方法进行实现)。所以ReentrantLock的FairSync对tryAcquire()方法进行了实现。
tryAcquire()返回true就代表获取独占锁资源成功:
@@ -314,16 +328,19 @@ tryAcquire()返回false代表获取独占锁资源失败,
那么就调用AQS.addWaiter()方法申请失败就将线程添加到等待队列尾部,AQS.acquireQueued()方法让这个线程进入阻塞等待状态(在阻塞之前如果等待队列只有这一个线程,是会先尝试着获取锁,失败才会进入阻塞状态。)
```java
+//公平锁的tryAcquire方法实现
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
+ //等待队列中没有线程,使用cas操作去抢锁,抢锁成功,就返回true
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
+ //当前线程与持有锁的线程是同一个,那么进行重入
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
@@ -359,6 +376,7 @@ protected final boolean tryRelease(int releases) {
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
+ //state为0就释放锁,否则只是锁的state减去releases
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
diff --git a/docs/MySQLNote.md b/docs/MySQLNote.md
index 10c116e..04c8b6b 100644
--- a/docs/MySQLNote.md
+++ b/docs/MySQLNote.md
@@ -79,15 +79,17 @@ innodb 引擎首先开启事务,获得一个事务ID(是一直递增的),根
因为需要保证更新后的唯一性,所以不能延迟更新,必须把数据页从磁盘加载到内存,然后判断更新后是否会数据冲突,不会的话就更新数据页。
-#### 5.写undo log(prepare状态)
+#### 5.写redo log(prepare状态)
-将对数据页的更改写入到redo log,将redo log设置为prepare状态。
+将对数据页的更改写入到redo log,此时redo log中这条事务的状态为prepare状态。
-#### 6.写bin log(commit状态)
+#### 6.写bin log(同时将redo log设置为commit状态)
-通知MySQL server已经更新操作写入到redo log 了,随时可以提交,将执行的SQL写入到bin log日志,将redo log改成commit状态,事务提交成功。
+通知MySQL server已经更新操作写入到redo log 了,随时可以提交,将执行的SQL写入到bin log日志,将redo log 中这条事务的状态改成commit状态,事务提交成功。
-#### undo log
+https://www.cnblogs.com/yuyue2014/p/6121114.html
+
+##### undo log
主要是保证事务的原子性,事务执行失败就回滚,用于在事务执行失败后,对数据回滚。
@@ -113,7 +115,7 @@ innodb 引擎首先开启事务,获得一个事务ID(是一直递增的),根
缺点是会造成内存脏页,后台线程会自动对脏页刷盘,或者是淘汰数据页时刷盘,此时会暂时查询操作,影响查询。
-#### 二段提交制
+##### 二段提交制是什么?
更新时,先改内存中的数据页,将更新操作写入redo log日志,此时redo log进入prepare状态,然后通知MySQL Server执行完了,随时可以提交,MySQL Server将更新的SQL写入bin log,然后调用innodb接口将redo log设置为提交状态,更新完成。
@@ -169,7 +171,7 @@ undo log是一种逻辑日志,是旧数据的备份。有两个作用用于事
##### redo log是什么?
-redo log用于保证数据的持久性。redo log记录的是数据页的物理变化,是新数据的备份,在事务提交前,将redo log 持久化就行,不需要将数据持久化,系统崩溃时,可以根据redo log将数据恢复到最新状态。
+redo log用于保证数据的持久性。redo log记录的是数据页的物理变化,是物理日志,是新数据的备份,在事务提交前,将redo log 持久化就行,不需要将数据持久化,系统崩溃时,可以根据redo log将数据恢复到最新状态。
redo log只做顺序追加操作,当事务需要回滚时,在redo log中也不会删除之前的事务记录。
@@ -193,6 +195,8 @@ redo log只做顺序追加操作,当事务需要回滚时,在redo log中也
保存的是逻辑日志,主要是存储每一条会修改数据的SQL。
+https://blog.csdn.net/qq_41652863/article/details/98800650
+
### MySQL中的事务是什么?
@@ -234,13 +238,13 @@ redo log只做顺序追加操作,当事务需要回滚时,在redo log中也
mvcc主要适用于可重复读,可以解决幻读的问题。
-innodb在解决幻读的问题主要是通MVVC 多版本并发版本控制来实现的
+innodb在解决幻读的问题主要是通MVVC 多版本并发版本控制来实现的。
就是每一行数据中额外保存两个隐藏的列:
**插入或上次更新该行的事务ID**(删除也被认为是一次更新,只不过在代表删除的更新操作中,行中的特殊位被设置为将其标记为已删除。这个事务ID可以认为是数据行的修改版本号。)
-**滚动指针**(指向undo log中用于事务回滚的日志记录)。
+**回滚指针**(指向undo log中用于事务回滚的日志记录)。
具体流程:
@@ -272,6 +276,8 @@ innodb在解决幻读的问题主要是通MVVC 多版本并发版本控制来实
https://dev.mysql.com/doc/refman/5.7/en/innodb-multi-versioning.html
+https://blog.csdn.net/qq_41652863/article/details/98800650
+
### MySQL是怎么解决幻读的问题的?
幻读就是事务执行过程中,在查询一个范围的数据时,有新的数据插入到这个范围,导致两次查询的数据不一致。因为读分为快照读和实时读,
@@ -396,7 +402,7 @@ innodb_autoinc_lock_mode为2
2.第二步只需要判断当前锁是否加了表级别的意向排斥锁,因为如果加了意向排斥锁,说明正在有事务在对数据加行锁,对数据进行更新,这样避免了对每一行数据进行判断,判断是否加了行锁。
-####Innodb的锁
+#### Innodb的锁
##### 行锁
@@ -465,7 +471,7 @@ https://blog.csdn.net/chai471793/article/details/99563704
### B树与B+树的区别是什么?
-B+树是为磁盘存储专门设计的一多阶平衡查找树,所有记录节点都是按照从小到大顺序存放在最后一层的叶子节点上,由各叶子节点的指针相连接。可以认为一个叶子节点就是一个内存页(默认情况下,一个内存页大小为16K),每个内存页里面存储多个数据行,内存页直接通过指针连接,形成一个**双向链表**,所以叶子节点在**逻辑上是连续的**,在**物理上不是连续存储的**,就是每个叶子节点可以存储在不同地址上,通过指针相互连接。每个非叶子节点(也就是索引节点)也是一个内存页,里面存储了很多索引节点的值,但是B+树索引节点只存索引值,不存数据行的数据,这样可以让每个索引内存页存储更多的索引值(这也是B+树比B树更优的地方)。B+树在数据库的中实现一般是只有2到4层,机械磁盘一般1秒可以进行100次IO,也意味着每次在B+树中的查询操作可以在20ms到40ms之间完成。
+B+树是为磁盘存储专门设计的一M阶多路平衡查找树(阶数可以理解为每个节点最多的孩子节点的个数,二叉树就是2阶),所有记录节点都是按照从小到大顺序存放在最后一层的叶子节点上,由各叶子节点的指针相连接。可以认为一个叶子节点就是一个内存页(默认情况下,一个内存页大小为16K),每个内存页里面存储多个数据行,内存页直接通过指针连接,形成一个**双向链表**,所以叶子节点在**逻辑上是连续的**,在**物理上不是连续存储的**,就是每个叶子节点可以存储在不同地址上,通过指针相互连接。每个非叶子节点(也就是索引节点)也是一个内存页,里面存储了很多索引节点的值,但是B+树索引节点只存索引值,不存数据行的数据,这样可以让每个索引内存页存储更多的索引值,这样可以使得B+树的层数更少(这也是B+树比B树更优的地方)。B+树在数据库的中实现一般是只有2到4层,机械磁盘一般1秒可以进行100次IO,也意味着每次在B+树中的查询操作可以在20ms到40ms之间完成。

@@ -794,7 +800,7 @@ SELECT a,b,c FROM user where a = 1
假如要取offset 为X,limitY的数据,
-1.假设有四个库,需要去每个库查offset为X,limit为Y的数据,然后得到四个结果集,每个结果集的time都有一个时间最小的time_min,时间最大的time_max结果,取四个结果集中最小的time_min
+1.假设有四个库,需要去每个库查offset为X/4,limit为Y的数据,然后得到四个结果集,每个结果集的time都有一个时间最小的time_min,时间最大的time_max结果,取四个结果集中最小的time_min
2.然后去每个库去查between time_min到之前每个库的time_max的结果集,
@@ -1016,35 +1022,26 @@ EXPLAIN SELECT * FROM res_user ORDER BYmodifiedtime LIMIT 0,1000
table | type | possible_keys | key |key_len | ref | rows | Extra EXPLAIN列的解释:
- table 显示这一行的数据是关于哪张表的
-- type 这是重要的列,显示连接使用了何种类型。从最好到最差的连接类型为const、eq_reg、ref、range、indexhe和ALL
+- type 这是重要的列,显示连接使用了何种类型。从最好到最差的连接类型为const、eq_reg、ref、range、index和ALL
+- ref 表里面的哪个索引被用到了
- rows 显示需要扫描行数
- key 使用的索引
-const就是针对主键或者唯一性索引的等值查询,通过索引查找一次就行了。仅仅是查一条数据。
-
-eq_ref 唯一性索引键扫描,对于每个索引键,表中只有一条数据与其对应,例如join查询时,对于前表查出的结果集,每个结果在第二个表只有一条数据对应。
-
-ref 非唯一性索引查询
-
- ref : 此类型通常出现在多表的 join 查询, 针对于非唯一或非主键索引, 或者是使用了 `最左前缀` 规则索引的查询. 关键字:非唯一性索引
-
- ref_or_null:与ref方法类似,只是增加了null值的比较。
-
- `range`: 表示使用索引范围查询, 通过索引字段范围获取表中部分数据记录. 这个类型通常出现在 =, <>, >, >=, <, <=, IS NULL, <=>, BETWEEN, IN() 操作中.
+ const就是针对主键或者唯一性索引的等值查询,通过索引查找一次就行了。仅仅是查一条数据。
- `index`: 表示全索引扫描(full index scan), 关键字:查询字段和条件都是索引本身
+ ref_eq 唯一性索引查询,一般使用一个表去join另外一个表的唯一性索引字段时,就是ref_eq
- index_merge:表示查询使用了两个以上的索引,最后取交集或者并集,常见and ,or的条件使用了不同的索引.效率不是很高 关键字:索引合并
+ ref 非唯一性索引查询,就是根据普通索引去查找数据,例如查询条件是where a = 1,a是普通索引,但是不是唯一性索引
- unique_subquery:用于where中的in形式子查询,子查询返回不重复值唯一值
+ range: 表示有范围条件的全索引范围查询,跟index全索引扫描相比就是有查询条件,可以减少一些扫描的数据量。同时除了显而易见的between,and以及'>','<'外,in和or也是索引范围扫描。
- index_subquery:用于in形式子查询使用到了辅助索引或者in常数列表,子查询可能返回重复值,可以使用索引将子查询去重。
-
- fulltext:全文索引检索,要注意,全文索引的优先级很高,若全文索引和普通索引同时存在时,mysql不管代价,优先选择使用全文索引
+ index: 表示全索引扫描(full index scan), 这是另外一种全表扫描,例如order by 字段是索引字段,如果是直接去聚集索引下全表扫描,那么查询出来的结果集还需要在内存中排序一边,如果是去非聚集索引下进行全表扫描,然后按照扫描顺序进行回表,回表的顺序就是order by的顺序,可以减少排序的时间,但是会有回表的开销。
all:这个就是全表扫描数据文件,然后再在server层进行过滤返回符合要求的记录。
```
+https://blog.csdn.net/dennis211/article/details/78170079
+
主要分为以下几个方法:
**1.减少请求的数据量**
@@ -1069,6 +1066,43 @@ ref 非唯一性索引查询
因为大查询在查询时可能会锁住很多数据,也需要获取到这些数据的行锁才能进行查询,切分成小查询可以减少锁竞争,减少等待获取锁的时间。
+##### 1.使用show profile对一条SQL查询分析当前会话中语句执行的资源消耗情况
+
+1.profiling配置默认是不开启的,可以使用set profiling = ON;命令将配置暂时打开。
+
+2.执行一条查询SQL
+
+3.使用show profiles可以查看最近15条查询SQL及对应的查询idquery id
+
+4.假设查询id为9,使用这个命令show profile for query 9;可以查看每个步骤及其消耗的时间。
+
+```
+mysql> show PROFILE for QUERY 9;
++----------------------+----------+
+| Status | Duration |
++----------------------+----------+
+| starting | 0.000054 |
+| checking permissions | 0.000007 |
+| Opening tables | 0.000116 |
+| init | 0.000019 |
+| System lock | 0.000009 |
+| optimizing | 0.000004 |
+| statistics | 0.000011 |
+| preparing | 0.000010 |
+| executing | 0.000002 |
+| Sending data | 0.000061 |
+| end | 0.000005 |
+| query end | 0.000006 |
+| closing tables | 0.000006 |
+| freeing items | 0.000031 |
+| cleaning up | 0.000010 |
++----------------------+----------+
+```
+
+https://www.cnblogs.com/116970u/p/11004431.html
+
+https://www.jianshu.com/p/1efdddf3d461
+
### char类型与varchar类型的区别?
char类型
@@ -1114,9 +1148,11 @@ SELECT * FROM test where id > 100 AND name = '123'
https://dev.mysql.com/doc/refman/5.7/en/index-condition-pushdown-optimization.html
##### Using filesort
-如果在关联表查询时,Order By的所有字段都来自第一个表(也就是驱动表),那么在处理驱动表时,从驱动表中取出满足条件的结果集时就会进行排序,不需要使用临时表存储数据行,进行重排序。
+
+如果在关联表查询时,Order By的所有字段都来自第一个表(也就是驱动表),那么在处理驱动表时,从驱动表中取出满足条件的结果集时就会进行排序,不需要使用临时表存储数据行进行重排序,extral那一列就会显示是Using index。如果Order By的字段不在索引中,那么就需要在查找出结果集后,进行重排序,就会显示Using filesort。
##### Using temporary; Using fileSort
-再进行关联表查询时,如果Order By的字段不全是来自驱动表,那么会把关联结果存放在临时表中,等所有的关联都结束后,再在内存中对数据行进行排序。
+
+再进行关联表查询时,如果Order By中的字段不全是来自驱动表,也就是使用了被驱动表中的字段进行排序,那么会把关联结果全部查找出来,存放在临时表中,等所有的关联都结束后,再在内存中对数据行进行排序。
##### 优化的方法:
@@ -1375,4 +1411,22 @@ table_io_waits_summary_by_index_usage这个表里面还有更详细的统计数
https://www.cnblogs.com/cchust/p/5057498.html
-https://www.cnblogs.com/cchust/p/5061131.html
\ No newline at end of file
+https://www.cnblogs.com/cchust/p/5061131.html
+
+### 数据库设计的三大范式是什么?
+
+1.确保数据库表的每一列的原子性
+
+保证每一列的值是原子性的,不可分割的,而不是在查询时需要分割使用函数来查询,降低查询效率。
+
+例如说有一个列是存储地址,如果你要根据省份查询,直接从地址中查可能会使用到函数,降低查询效率,最好是单独将省份另外存一份。
+
+2.确保数据库表的每一列都与主键相关
+
+就是表里面只应该存储一种数据,而不是多种数据,例如订单表就应该存储订单,其中包含商品的id就行了,而不是存储一些商品的具体名称,价格等信息。避免数据冗余,增大维护成本。
+
+3.确保数据库表的每一列直接依赖主键,而不是间接依赖主键。
+
+这一条其实更第二范式差不多。
+
+https://www.cnblogs.com/linjiqin/archive/2012/04/01/2428695.html
\ No newline at end of file
diff --git a/docs/Nginx.md b/docs/Nginx.md
index 529c722..31c2a19 100644
--- a/docs/Nginx.md
+++ b/docs/Nginx.md
@@ -1,7 +1,8 @@
(PS:扫描[首页里面的二维码](README.md)进群,分享我自己在看的技术资料给大家,希望和大家一起学习进步!)
#### [1.nginx负载均衡算法有哪些?](#nginx负载均衡算法有哪些?)
-#### [2. 一致性hash是什么?](# 一致性hash是什么?)
+#### [2.一致性hash是什么?](#一致性hash是什么?)
+#### [3.有哪些实现限流的技术方案?](#有哪些实现限流的技术方案?)
### nginx负载均衡算法有哪些?
@@ -103,9 +104,7 @@ upstream somestream {
}
```
-### nginx怎么实现限流?
-
-有哪些算法限流算法?
+### 有哪些实现限流的技术方案?
#### 1.计数器(固定窗口限流+滑动窗口限流)
@@ -376,4 +375,7 @@ Demo3.抢购场景降级
3)(integer)14 # 漏斗剩余容量
4)(integer)-1 # 被拒绝之后,多长时间之后再试(单位:秒)-1 表示无需重试
5)(integer)2 # 多久之后漏斗完全空出来
-```
\ No newline at end of file
+```
+
+
+
diff --git a/docs/RedisBasic.md b/docs/RedisBasic.md
index 1af3de9..e5634f2 100644
--- a/docs/RedisBasic.md
+++ b/docs/RedisBasic.md
@@ -31,7 +31,7 @@ Redis是一个开源的,基于内存的,也可进行持久化的,使用C
##### (2)定时清理
-Redis配置项hz定义了serverCron任务的执行周期,默认每次清理时间为25ms,每次清理会依次遍历所有DB,从db随机取出20个key,如果过期就删除,如果其中有5个key过期,说明过期率超过了25%,那么就继续对这个db进行清理,否则开始清理下一个db。
+Redis配置项hz定义了serverCron任务的执行周期,默认每次清理时间为25ms,每次清理会依次遍历所有DB,从db的expires字典(里面保存了设置了过期时间的键值对,key就是指向键对象,value是过期时间)中随机取出20个key,如果过期就删除,如果其中有5个key过期,说明过期率超过了25%,那么就继续对这个db进行清理,否则开始清理下一个db。
##### (3)内存不够时清理
@@ -247,10 +247,21 @@ http://blog.csdn.net/lzb348110175/article/details/98941378
https://xie.infoq.cn/article/b3816e9fe3ac77684b4f29348
+### epoll水平触发和边缘触发的区别?
+
+**水平触发**和**边缘触发**两种:
+
+- LT,默认的模式(水平触发) 只要该fd还有数据可读,每次 `epoll_wait` 都会返回它的事件,提醒用户程序去操作,
+- ET是“高速”模式(边缘触发)
+
+ 只会提示一次,直到下次再有数据流入之前都不会再提示,无论fd中是否还有数据可读。所以在ET模式下,read一个fd的时候一定要把它的buffer读完,即读到read返回值小于请求值或遇到EAGAIN错误
+
+epoll使用“事件”的就绪通知方式,通过`epoll_ctl`注册fd,一旦该fd就绪,内核就会采用类似回调机制激活该fd,`epoll_wait`便可收到通知。
+
### 同步与异步的区别是什么?
同步与异步的区别在于调用结果的通知方式上。
-同步执行一个方法后,需要等待结果返回,然后继续执行下去。
-异步执行一个方法后,不会等待结果的返回,调用方定时主动去轮询调用结果或者被调用方在执行完成后通过回调来通知调用方。
+同步执行一个方法后,需要等待结果返回轮询调用结果才能继续执行,然后继续执行下去。
+异步执行一个方法后,不会等待结果的返回,被调用方在执行完成后通过回调来通知调用方继续执行。
### 阻塞与非阻塞的区别是什么?
阻塞与非阻塞的区别在于进程/线程在等待消息时,进程/线程是否是挂起状态。
@@ -261,6 +272,32 @@ https://xie.infoq.cn/article/b3816e9fe3ac77684b4f29348
在消息发出去后,不会阻塞当前进/线程,而会立即返回,可以去执行其他任务。
+### BIO,NIO,AIO有什么区别?
+
+一次IO的读操作分为等待就绪和IO操作两个阶段,等待就绪就是等待TCP RecvBuffer里面的数据就绪好,也就是发送方的数据全部发送到网卡里面来,操作就是CPU将数据从网卡拷贝到用户空间。
+
+**BIO(同步阻塞型)**
+
+以Java中的IO方式为例,BIO就是同步阻塞型的IO,当一个Socket连接发送数据过来以后,需要为这个Socket连接创建一个线程,由线程调用Socket的read()方法读取数据,需要先把数据从网卡拷贝到内核空间,再拷贝到用户态的内存空间,在数据读取完成前,线程都是阻塞的。这种IO方式就是比较消耗资源,假设有1000个活跃的Socket连接,需要创建出1000个线程来读取数据,读取时都是阻塞的,每个线程有自己独有的线程栈,默认大小是1M。
+
+**NIO(同步非阻塞型)**
+
+nio就是多路io复用,就是一个线程来处理多个Socket连接,节省线程资源。以select为例,就是有一个长度为1024的数组,每个元素对应一个Socket连接,线程轮询这个数据,判断哪个Socket连接的数据是出于就绪状态,此时就将数据拷贝到用户空间然后进行处理。由于IO操作阶段是需要等待数据拷贝到用户空间完成才能返回,所以是同步的。由于每次判断内核中Socket缓冲区的数据是否就绪的函数是直接返回的,如果就绪就返回有数据,不是就绪就返回0,线程不需要阻塞等待,所以是非阻塞的。
+
+**AIO(异步非阻塞型)**
+
+AIO就是NIO的升级版,在数据处于就绪状态时,也是异步将Socket缓冲区中的数据拷贝到用户空间,然后执行异步回调函数,所以在IO操作阶段也是异步的。
+
+
+
+BIO里用户最关心“我要读”,NIO里用户最关心"我什么时候可以读了",在AIO模型里用户更需要关注的是“什么时候读完了,我可以直接进行处理了”。
+
+https://zhuanlan.zhihu.com/p/23488863
+
+https://www.cnblogs.com/sxkgeek/p/9488703.html
+
+https://blog.csdn.net/weixin_34378045/article/details/91930797
+
### 如何解决Redis缓存穿透问题?
Redis 缓存穿透指的是攻击者故意大量请求一些Redis缓存中不存在key的数据,导致请
求打到数据库上,导致数据库压力过大。
@@ -291,7 +328,7 @@ Redis 缓存穿透指的是攻击者故意大量请求一些Redis缓存中不存
1.在给缓存设置失效时间时加一个随机值,避免集体失效。
-2.双缓存机制,缓存A的失效时间为20分钟,缓存B没有失效时间,从缓存A读取数据,缓存A中没有时,去缓存B中读取数据,并且启动一个异步线程来更新缓存A。
+2.双缓存机制,缓存A的失效时间为20分钟,缓存B的失效时间会比A长一些,从缓存A读取数据,缓存A中没有时,去缓存B中读取数据,并且启动一个异步线程来更新缓存A(如果已经有异步线程正在更新了,就不用重复更新了)。以及更新缓存B,以便延迟B的过期时间。
### 如何解决缓存与数据库的数据一致性问题?
diff --git a/docs/RedisDataStruct.md b/docs/RedisDataStruct.md
index 051b902..5cd40d6 100644
--- a/docs/RedisDataStruct.md
+++ b/docs/RedisDataStruct.md
@@ -23,6 +23,10 @@ Redis中的简单动态字符串其实是对C语言中的字符串的封装和
2.频繁修改一个字符串时,会涉及到内存的重分配,比较消耗性能。(Redis中的简单动态字符串会有内存预分配和惰性空间释放)。
+**如果字符串实际使用长度len<1M**,实际分配空间=len长度来存储字符串+1字节存末尾空字符+len长度的预分配空闲内存
+
+**如果字符串实际使用长度len>1M**,实际分配空间=len长度来存储字符串+1字节存末尾空字符+1M长度的预分配空闲内存
+
所以Redis中的简单动态字符串结构,除了包含一个字符数组的属性,还包含数组的长度,数组的实际使用长度等属性,通过增加长度属性,可以保证字符串是二进制安全的,从而可以保存任意类型的数据,例如一张图片,对象序列化后的数据等等。
##### 字符串使用场景如下:
@@ -119,15 +123,16 @@ Set是一个无序的,不重复的字符串集合,底层编码有inset和has
当元素个数较多时,Set使用hashtable来保存元素,元素的值作为key,value都是NULL。
### 谈一谈你对Redis中有序集合ZSet的理解?
+
Zset与Set的区别在于每一个元素都有一个Score属性,并且存储时会将元素按照Score从低到高排列。底层是通过跳跃表实现的。
##### ziplist
-当元素较少时,ZSet的底层编码使用ziplist实现,所有元素按照Score从低到高排序。
+当元素较少时(元素个数<128个,且每个元素的长度小于64字节),ZSet的底层编码使用ziplist实现,所有元素按照Score从低到高排序。
##### skiplist+dict
-当元素较多时,使用skiplist+dict来实现,
+当元素较多时,使用skiplist+dict来实现。
skiplist存储元素的值和Score,并且将所有元素按照分值有序排列。便于以O(logN)的时间复杂度插入,删除,更新,及根据Score进行范围性查找。
dict存储元素的值和Score的映射关系,便于以O(1)的时间复杂度查找元素对应的分值。
diff --git a/docs/RedisStore.md b/docs/RedisStore.md
index bcc30bf..6b3716c 100644
--- a/docs/RedisStore.md
+++ b/docs/RedisStore.md
@@ -15,7 +15,7 @@
#### AOF持久化
-AOF持久化主要是Redis在修改相关的命令后,将命令添加到aof_buf缓存区(aof_buf是Redis中的SDS结构,SDS结构可以认为是对C语言中字符串的扩展)的末尾,然后在每次事件循环结束时,根据appendfsync的配置(always是总是写入,everysec是每秒写入,no是根据操作系统来决定何时写入),判断是否需要将aof_buf写入AOF文件。生产环境中一般用默认配置everysec,也就是每秒写入一次,一旦挂机会丢掉1分钟的数据。
+AOF持久化主要是Redis在修改相关的命令后,将命令添加到aof_buf缓存区(aof_buf是Redis中的SDS结构,SDS结构可以认为是对C语言中字符串的扩展)的末尾,然后在每次事件循环结束时,根据appendfsync的配置(always是每次事件循环都将aof_buf缓冲区的内容写入,everysec是每秒写入,no是根据操作系统来决定何时写入),判断是否需要将aof_buf写入AOF文件。生产环境中一般用默认配置everysec,也就是每秒写入一次,一旦挂机会丢掉1分钟的数据。
```c
struct redisServer {
diff --git a/docs/RedisUserful.md b/docs/RedisUserful.md
index c4caea9..2695ea5 100644
--- a/docs/RedisUserful.md
+++ b/docs/RedisUserful.md
@@ -104,7 +104,7 @@ Redis中的哨兵服务器是一个运行在哨兵模式下的Redis服务器,

-##### Redis Cluster的节点扩容和下线
+#### Redis Cluster的节点扩容和下线
##### 扩容
@@ -147,7 +147,7 @@ https://www.cnblogs.com/youngchaolin/archive/2004/01/13/12034660.html
由于 Redis Cluster 的节点不断地与集群内的节点进行通讯,下线信息也会通过 Gossip 消息传遍所有节点。
-因此集群内的节点会不断收到下线报告,当半数以上持有槽的主节点标记了某个节点是主观下线时,便会任务节点2**客观下线**,执行后面的流程。
+因此集群内的节点会不断收到下线报告,当半数以上持有槽的主节点标记了某个节点是主观下线时,便会认为节点2**客观下线**,执行后面的流程。
##### 3.资格检查
@@ -171,11 +171,13 @@ https://www.cnblogs.com/youngchaolin/archive/2004/01/13/12034660.html
说白了更新这个值目的是,保证所有主节点对这件“大事”保持一致。大家都统一成一个配置纪元(一个整数),表示大家都知道这个“大事”了。
-更新完配置纪元以后,每个从节点会向集群内发起广播选举的消息(FAILOVER_AUTH_REQUEST)。并且保证每个从节点在一次配置纪元中只能发起一次选举。
+更新完配置纪元以后,每个从节点会向集群内发起广播选举的消息。
##### 6.主节点为选举投票
-参与投票的只有主节点,从节点没有投票权,超过半数的主节点通过某一个节点成为新的主节点时投票完成。
+参与投票的只有主节点,从节点没有投票权。每个主节点在收到从节点请求投票的信息后,如果它还没有为其他从节点投票,那么就会把票投给从节点。(也就是主节点的票只会投给第一个请求它选票的从节点。)
+
+超过半数的主节点通过某一个节点成为新的主节点时投票完成。
如果在 cluster-node-timeout*2 的时间内从节点没有获得足够数量的票数,本次选举作废,进行第二轮选举。
diff --git a/docs/Rule.md b/docs/Rule.md
new file mode 100644
index 0000000..0d5e10a
--- /dev/null
+++ b/docs/Rule.md
@@ -0,0 +1,78 @@
+##### 1.使用BigDecimal替代Float和Double
+
+主要Float和Double是使用类似于科学计数法那样"有效数字+指数"来表示的,所以在二进制存储时,是会丢失精度,没法做到精准的。所以浮点数之间不能使用==等值判断,浮点数包装类型之间不能使用equals
+
+```java
+float a = 1.0f - 0.9f;
+float b = 0.9f - 0.8f; //通过debug调试发现这两次减运算减下来,a和b的值存在一些差异,不是一模一样的,丢失了精度
+Boolean result = a == b;//结果是false
+
+Float x = 1.0F - 0.9F;
+Float y = 0.9F - 0.8F;
+Boolean result = x.equals(y);//结果是false
+```
+
+解决方案:
+
+1. 任何货币金额,均以最小货币单位且整型类型来进行存储。例如在数据库里面存10000,代表是100.00元。
+
+2. 使用Bigdecimal来存这些值,并且进行加减。
+
+```java
+BigDecimal a1 = new BigDecimal("1.0");
+BigDecimal b1 = new BigDecimal("0.9");
+BigDecimal c1 = new BigDecimal("0.8");
+
+BigDecimal a2 = a1.subtract(b1);
+BigDecimal b2 = b1.subtract(c1);
+
+System.out.println(a2.equals(b2));//打印结果是true
+```
+
+
+
+2.布尔类型的变量,都不要加is前缀,否则部分框架解析会引起序列化错误。
+
+一般假设我们定义一个Boolean变量为isHot,按照Bean规范生成的get方法应该是isIsHot,但是我们使用IDEA来自动生成get方法时,生成的是isHot()方法,
+
+> JavaBeans规范中对这些均有相应的规定,基本数据类型的属性,其getter和setter方法是getXXX()和setXXX,但是对于基本数据中布尔类型的数据,又有一套规定,其getter和setter方法是isXXX()和setXXX。但是包装类型都是以get开头。
+
+```java
+private boolean isHot;
+public boolean isHot() {
+ return isHot;
+}
+public void setHot(boolean hot) {
+ isHot = hot;
+}
+```
+
+这样子在实例对象转json时,序列化框架看到isHot()方法会去找hot实例变量,这样就会找不到变量值。
+
+在与前端交互时,同意需要注意,避免Boolean类型参数是is前缀开头的,因为Spring MVC在接受参数时,看到前端json传过来的值是isHot,而set方法中没有setIsHot()方法,只有setHot()方法
+
+https://www.cnblogs.com/goloving/p/13086151.html
+
+https://blog.csdn.net/qq_31145141/article/details/71597608
+
+##### 正确的加锁方式
+
+通过这种方式来加锁,保证抛出异常时可以正常解锁,同时lock()方法不能在try代码块中调用,防止线程还没有加上锁时抛出异常,然后进行解锁,抛出IllegalMonitorStateException异常(对未加锁对象调用释放锁tryRelease()方法就会抛这个异常)。
+
+```java
+Lock lock = new XxxLock(); // ...
+lock.lock();
+try {
+ doSomething();
+ doOthers();
+} finally {
+ lock.unlock();
+}
+```
+
+##### COUNT(*)与count(列名)
+
+1.【强制】不要使用count(列名)或count(常量)来替代count(*),count(*)是SQL92定义的标 准统计行数的语法,跟数据库无关,跟 NULL 和非 NULL 无关。
+ 说明:count(*)会统计值为 NULL 的行,而 count(列名)会将NULL值排除掉,不会统计此列为 NULL 值的行。
+
+2.count(distinct col) 计算该列除 NULL 之外的不重复行数
\ No newline at end of file
diff --git a/docs/Spring.md b/docs/Spring.md
index 726876f..fb814cf 100644
--- a/docs/Spring.md
+++ b/docs/Spring.md
@@ -3,6 +3,8 @@
#### [1.SpringAOP是怎么实现的?](#SpringAOP是怎么实现的?)
### SpringAOP是怎么实现的?
+Spring Aop是一种可以减少大量重复代码的一种编程技术,可以设置一个切面,比如说是某个包下面的所有方法,这些方法在执行的时候就会调用我们写的拦截方法,我们可以做一些类似于日志打印等一些操作。
+
实现AOP有三种方式:静态代理,使用JDK的Proxy类实现动态代理,使用CGLIB实现动态代理。
#### 静态代理
@@ -169,3 +171,165 @@ IOC就是invention of control,就是控制反转,将对象获取外界依赖
DI(Dependency Injection,依赖注入)其实就是IOC的另外一种说法,就是IOC是通过依赖注入技术实现的。
《跟我学spring3系列》https://www.iteye.com/blog/jinnianshilongnian-1413851
https://www.cnblogs.com/xdp-gacl/p/4249939.html
+
+### Spring IOC是怎么解决循环依赖问题的?
+
+Spring IOC只能解决属性注入之间的循环依赖问题,如果是构造器之间的循环依赖,只会抛出BeanCurrentlyInCreationException异常。
+
+Spring使用了3个Map来保存Bean,俗称为三级依赖:
+
+singletonObjects 一级缓存,用于保存实例化、注入、初始化完成的bean实例,可以使用的。
+
+earlySingletonObjects 二级缓存,bean刚刚构造完成,但是还没有进行属性填充。
+
+singletonFactories 三级缓存,用于保存正在创建中的bean,以便于后面扩展有机会创建代理对象,此时的bean是没有完成属性填充的。
+
+假设A类和B类相互依赖,A中有一个B类的属性,B中有一个A类的属性。那么在初始化A的Bean时,首先会依次去一级依赖,去二级依赖,三级依赖中去找,都没有就调用创建方法创建实例A,将A添加到三级依赖中,然后对A的属性进行依赖注入,填充属性时,发现B的Bean在各级依赖中都没有,就创建B的bean添加到三级依赖,然后对B的属性进行填充,填充B的属性A时,会从三级依赖中取出A,填充完放到二级依赖,然后对B进行初始化,初始化完成添加到一级依赖。B初始化完成后,将B从一级依赖中,填充到实例A,A可以进入到二级依赖,完全初始化完成后,A进入到一级依赖,供用户代码使用。
+
+
+
+
+
+https://juejin.cn/post/6911692836714840077
+
+https://segmentfault.com/a/1190000015221968
+
+https://blog.csdn.net/qq_35165000/article/details/108185093?spm=1001.2014.3001.5501
+
+### Bean的生命周期是怎么样的?
+
+Bean的生命周期主要分为以下四个阶段:
+
+1.**Bean的实例化阶段**-主要是在createBeanInstance()方法中,调用类的构造器方法来创建一个Bean实例。用户可以自定义一个类,继承InstantiationAwareBeanPostProcessorAdapter,重写它的两个方法,对Bean的实例化前后做一些额外的操作,例如打印日志。
+
+```java
+public class MyInstantiationAwareBeanPostProcessorAdapter extends InstantiationAwareBeanPostProcessorAdapter {
+ @Override
+ public Object postProcessBeforeInstantiation(Class> beanClass, String beanName) throws BeansException {
+ if (beanName.equals("car")) {
+ System.out.println(beanName + "在实例化之前");
+ }
+ return super.postProcessBeforeInstantiation(beanClass, beanName);
+ }
+ @Override
+ public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
+ if (beanName.equals("car")) {
+ System.out.println(beanName + "在实例化之后");
+ }
+ return super.postProcessAfterInstantiation(bean, beanName);
+ }
+}
+```
+
+2.**属性赋值阶段**-主要是在populateBean()方法中,对Bean的各项属性进行赋值。
+
+3.**Bean的初始化阶段**-主要调用用户自定义的初始化方法init-Method()
+
+用户可以自定义一个类,继承BeanPostProcessor,重写它的两个方法,对Bean的初始化前后做一些额外的操作,例如打印日志。
+
+```java
+public class NdBeanPostProcessor implements BeanPostProcessor {
+ public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
+ System.out.println("NdBeanPostProcessor 在" + beanName + "对象初始化之前调用......");
+ if (beanName.equals("car")) {
+ return new CglibInterceptor().getIntance(bean.getClass());
+ }
+ return bean;
+ }
+ public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
+ System.out.println("NdBeanPostProcessor 在" + beanName + "对象初始化之后调用......");
+ return bean;
+ }
+}
+```
+
+4.**Bean销毁阶段**,用户可以自定义destroyMethod()方法,在Bean被销毁时被调用。
+
+### BeanFactory和FactoryBean有什么区别?
+
+**BeanFactory**是一个接口,定义了IOC容器的最基本的规范,并提供了IOC容器应遵守的的最基本的方法。在Spring代码中,BeanFactory只是个接口,并不是IOC容器的具体实现,但是Spring容器给出了很多种实现,如 DefaultListableBeanFactory、XmlBeanFactory、ApplicationContext等,都是附加了某种功能的实现。
+
+```java
+package org.springframework.beans.factory;
+import org.springframework.beans.BeansException;
+public interface BeanFactory {
+ String FACTORY_BEAN_PREFIX = "&";
+ Object getBean(String name) throws BeansException;
+ T getBean(String name, Class requiredType) throws BeansException;
+ T getBean(Class requiredType) throws BeansException;
+ Object getBean(String name, Object... args) throws BeansException;
+ boolean containsBean(String name);
+ boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
+ boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
+ boolean isTypeMatch(String name, Class> targetType) throws NoSuchBeanDefinitionException;
+ Class> getType(String name) throws NoSuchBeanDefinitionException;
+ String[] getAliases(String name);
+}
+```
+
+**FactoryBean**是一个接口,有一个创建bean对象的方法getObject(),当一些bean对象不能由ioc容器简单得调用类的构造器方法来创建实例对象时使用,可以将Bean类实现FactoryBean接口,实现getObject()方法,供ioc容器调用来创建bean对象。
+
+```java
+public interface FactoryBean {
+ @Nullable
+ T getObject() throws Exception;
+
+ @Nullable
+ Class> getObjectType();
+
+ default boolean isSingleton() {
+ return true;
+ }
+}
+```
+
+https://www.cnblogs.com/aspirant/p/9082858.html
+
+### Springboot启动过程
+
+##### 构造SpringApplication实例
+
+1.首先会调用SpringApplication的静态方法run(),在这个方法里面会调用构造器方法创建出一个SpringApplication实例,在构造器中会确定当前web应用类型,是reactive web类型,还是servlet web类型,还是none类型。以及设置监听器等等,完成一些初始化操作。(监听器就是来监听SpringApplication启动过程的,在开始启动,创建上下文,启动失败等生命周期事件时都会调用监听器相关的方法)
+
+##### 执行run()方法
+
+2.然后去执行实例的run()方法,首先会创建一个StopWatch计时器器,来统计run()方法的启动耗时,在日志里面会显示启动时间,那个时间就是在这里统计的。然后处理环境参数,就是`java -jar ***.jar`启动命令中带的那些jvm参数。
+
+
+
+##### 创建applicationContext
+
+3.会创建出一个ApplicationContext,一般servlet的应用的context类型是AnnotationConfigServletWebServerApplicationContext。(可以认为beanFactory就是ioc容器,但是我们一般不直接使用beanFactory获取bean,而是通过applicationContext来获取,ioc容器beanFactory是应用上下文applicationContext的一个属性,applicationContext也实现了BeanFactory接口,可以认为applicationContext是一个高级容器,applicationContext支持国际化,默认是启动时加载所有bean,而不是用到时才进行懒加载,以及支持事件机制。)
+
+##### 执行prepareContext()方法
+
+4.然后会调用prepareContext()方法来为应用上下文做一些准备工作,会将运行时的参数封装成bean,注册到beanFactory中去,以及使用load方法加载启动类。
+
+##### 执行refreshContext()方法
+
+5.在这里会启动容器,也就是会为beanFactory做很多配置,注册BeanPostProcessors,设置类加载器等等。在这一步也会解析启动类中@SpringBootApplication这个组合注解。
+
+##### afterRefresh()方法
+
+6.这个方法里面会把容器里面所有ApplicationRunner自定义子类和CommandLineRunner自定义子类的Bean全部取出来,执行它们的run()方法。(就是有时候如果需要在应用启动后执行一些我们自定义的初始化操作,可以通过自定义一个类,继承ApplicationRunner类来实现。)
+
+之后会调用listeners.started()方法,通知所有Listener,application已经启动完成了,以及调用listeners.running()方法通知所有Listener,application已经运行了。
+
+```java
+//系统启动完可以做一些业务操作
+@Component
+//如果有多个runner需要指定一些顺序
+@Order(1)
+public class SimosApplicationRunner implements ApplicationRunner {
+@Autowired
+SystemInitService systemInitService;
+@Override
+public void run(ApplicationArguments args) throws Exception {
+ systemInitService.systemInit();
+}
+}
+```
+
+https://my.oschina.net/funcy/blog/4873261
+
+https://www.imooc.com/article/264722
\ No newline at end of file
diff --git a/docs/SystemDesign.md b/docs/SystemDesign.md
index a7e6df2..ff1b842 100644
--- a/docs/SystemDesign.md
+++ b/docs/SystemDesign.md
@@ -2,7 +2,10 @@
#### [1.怎么设计一个大文件上传的功能?](#怎么设计一个大文件上传的功能?)
+#### [2.谈一谈分布式ID生成方案的了解?](#谈一谈分布式ID生成方案的了解?)
+
### 怎么设计一个大文件上传的功能?
+
首先如果是大文件上传,考虑到网络不稳定容易造成上传失败,或者需要做断点续传功能,是不是对整个文件直接进行上传的。
#### 1.计算hash
@@ -33,36 +36,41 @@ https://zhuanlan.zhihu.com/p/104826733
有两种方案,一种是后端记录所有分片的上传状态,当所有分片全部上传完毕后,自动合并文件。另外一种就是前端发现传完所有文件后,调用接口通知后端去合并。
-### 谈一谈ID生成方案的了解?
+### 谈一谈分布式ID生成方案的了解?
首先ID生成方案的技术考虑点:
唯一性:不能重复。
趋势递增:保证id作为主键时,插入数据库时的顺序性,避免随机插入。
单调递增:满足某些特殊业务的要求,保证后一秒请求生成的id比前一秒的大。
信息安全性:不要像UUID一样泄露mac地址,也不要像数据库主键ID自增完全连续,泄露每日id生成数据量。
-##### UUID
+#### 1.UUID模式
+
太长,占用存储空间过大
id是字符串类型,查询成本高于数字类型
如果是包含mac地址的UUID会泄密
-##### 单机数据库主键自增
+#### 2.单机数据库主键自增模式
+
id生成的量受限于单机MySQL数据库的性能
强依赖于数据库,主从切换时容易导致重复发号
容易泄露id生成量
-##### 多机数据库主键自增
-不便于扩展
+#### 3.多机数据库主键自增模式
+
+主要是将每个数据库的步长设置为一样,但是起始值不一样,以此错开生成的id,不便于扩展。
+
+##### 4.Leaf号段模式模式
-##### Leaf号段模式
就是数据库存储一个maxId代表已经发放的id最大值,每次将maxId更新为max+step,取step数量的id发放。
优点:
-1.便于扩展,发号性能取决于Step,可以动态调整。(Leaf做了Step动态调整策略,15分钟内消耗了一个号段,就让号段拥有的id量翻倍(直到最大阀值100万),否则减半(直到最低阀值,初始号段id量)。)
+1.便于扩展,发号性能取决于step,可以动态调整。(Leaf做了Step动态调整策略,一个号段使用时间<15分钟,就让号段拥有的id量step翻倍(直到最大阀值100万),一个号段使用时间>30分钟,step减半(直到最低阀值,初始号段id量)。)
2.即便主节点的宕机,短时间Leaf也能继续提供服务,其次是主从切换时影响较小。
缺点:
1.当号段里面的id用完时,会去数据库取新的号段,此时如果来了获取id的请求会需要进行等待。(Leaf做了双Buffer优化,使用了双Buffer各存储一个号段,当一个号段使用量达到10%后,就触发另一个号段去数据库取新号段进行更新,以便于当一个号段使用完时,可以直接切换到未使用的号段。)
2.id是连续的,容易泄密。(可以自定义抛弃策略,取号段时的时候抛弃一些id,或者定时抛弃掉一些id。)
-##### Leaf-snowflake模式
+#### 5.Leaf-snowflake模式
+
就是沿用了snowflake原本的位数分配算法,1标志位+41位毫秒时间戳+10位机器位+12位序列号。使用zookeeper作为注册中心,id生成服务启动时,去指路径下获取所有节点的列表,判断当前ip+port是否有对应的workid存在,有就使用,没有就往插入一个新的永久顺序节点,序号则为workId(并且会将workid缓存到本地磁盘上)。运行期间每过3s,都会上报最新的时间戳到zookeeper。
**时钟回滚的处理:**
**启动时:**
@@ -72,7 +80,199 @@ zookeeper里面会存上次生成id的时间戳,如果上次存储的时间戳
优点:
+**时钟回滚优化**
+
1.对时钟回滚做了特殊处理。
+**zookeeper弱依赖**
+
2.为了减轻了zookeeper的弱依赖,实现在zookeeper挂了的情况下,id生成服务也能启动,每次启动后,在本地也缓存workid配置,一旦启动时,发现zookeeper连接不上,就通过从本地缓存配置中读取workid。(但是这样也有问题,本地缓存配置只存了workid,没有存上次生成id的最大时间戳,所以一旦启动前发生了时钟回滚,或者是修改了系统时间,这样从本地缓存配置中读取workid生成的id就可能是重复的。)
+**时间差优化**
+
+3.做了时间差优化,就是默认的时间戳是从1970年开始的,leaf是自己选定了2010年的一个时间点,以此来计算时间戳,这样可以在时间位数固定的情况下,增长服务最大运行时间。在毫秒时间戳为41位的情况下,时间差最大是69年,如果以1970年为起点,那么最大时间就是1970+69年,如果以2010年为起点就是2010+69年。
+
+**序列号优化**
+
+4.为了防止生成的id的序列号部分都是从0开始,导致插入数据库时,有数据倾斜的问题,所以每次用新的毫秒时间戳时,序列号不是从0开始,而是计算一个0到100之间的随机数作为起点。
+
+缺点:
+
+1.注册中心只支持Zookeeper
+
+2.潜在的时钟回拨问题
+
+3.时间差过大时,生成id为负数。
+
+### 6.百度的uid-generator模式
+
+#### 默认模式
+
+每次启动时向数据库插入一条数据,这行数据的主键是自增的,主键id就是workId,
+
+因为默认是snowflake算法是**1标志位+41位时间戳+10位机器号+12位序列号**,
+
+因为百度的是每次启动都获取新的机器号,所以它修改了这些位数配比,是
+
+**1标志位+28位的秒级时间差+22位的机器号+13位的序列号**,所以总共支出2的22次方次启动,也就是400万次启动。最大服务支持时间是2*28次方,也就是8.7年。(优化点是修改位数分配,让服务时间更长,我们的位数分配是**30位秒级时间差+16位机器号+7位序列号**,最长服务时间支持34年,6.5w次机器启动,每个机器每秒128个并发,总共位数没有用到64位,只用到53位,这样生成的id转化为10进制更小。)
+
+解决时间回拨问题:
+
+* 启动时时间回拨
+
+因为是每次都用新的机器号,所以当前机器号都是之前没有的,所以即便时间戳回拨也不影响。
+
+* 运行时时间回拨
+
+会使用lastSecond来记录上次生成id的时间戳,如果当前时间戳比lastSecond还小,就抛出异常。(优化点就是**回拨时间差较小时进行等待**,较长时再抛出异常。)
+
+缺点:
+
+1.默认的最大服务年限太短,只有8年。
+
+2.回拨时间差较小时也是抛出异常,没有额外的判断逻辑。
+
+3.没有像Leaf一样做序列号优化,可能生成的id序列号部分都是从0开始的多一些,可能会存在数据倾斜的问题。
+
+#### 缓存模式
+
+主要继承自默认模式,只是用一个环形数组来存储生成好的id,每次去环形数组中去,默认大小是2的13次方,8192。这种模式使用的时间取得不是实时的系统时间,而且使用启动时的时间,每次生成一组id时,对之前保存的时间+1。
+
+**阀值检测预填充**:取id时,发现可用id数小于阀值50%时,就对后面已经使用的id进行再填充。
+
+**定期填充**:每5分钟定期会去检查环形数组中id使用情况,然后生成一组最大序列号个数的id(默认是8192个),然后进行填充,多的直接丢弃掉。
+
+**缺点**:
+
+1.id只有在定期填充时,会丢弃掉一些id,其他情况下,id是完全连续的。假如每次使用量比较大,大部分时候都是5分钟内能用掉50%的话,那么就就不会触发定期填充,也没有id丢弃,导致id会一直连续,容易泄露数据信息,所以最好自定义丢弃逻辑。
+
+2.其次是id跟生成id时系统的时间戳无关了,可能无法满足一些特殊业务的需求。
+
+### 自己的方案
+
+我们自己的主要是根据uid-generator来做的,因为只是启动的时候依赖数据库,不需要引进新的依赖。做的优化主要是修改了位数分配,使得支持最大服务年限更长,30位时间差+16位机器号+7位序列号。做了时钟回拨优化,回拨差值较小时进行等待。以及做了序列号丢弃的优化。
+
+### 谈一谈你对分布式事务的理解?
+
+#### 2PC方案(2阶段提交制)
+
+
+
+这种方案就是引入了一个协调者,主要分为prepare和commit两个阶段,在第一阶段其实就是协调者给各个业务系统发送命令,业务系统收到后,就会开始执行这个子事务的具体操作,然后给协调者反馈,子事务执行成功还是失败,发送后,业务系统就会同步阻塞等待协调者的第二阶段的指令。
+
+协调者第一阶段发送指令会进入超时等待状态,等待各个业务系统返回子事务的执行结果。
+
+**执行成功,提交**
+
+如果所有业务系统都执行成功了,并且协调者收到了反馈,那么就认为整个事务执行成功了,就会通知各个业务系统进行提交。
+
+**执行失败,回滚**
+如果超过时间还没有收到所有业务系统返回的结果,或者是有业务系统返回了执行失败的结果,那么协调者就认为执行失败了,通知各个业务系统进行回滚,如果此时网络出现阻塞,业务系统收不到通知,那么协调者就会一直重试发送回滚的指令。
+
+**缺点**
+
+1.单点问题:就是一旦协调者挂掉,所有子系统都会进入阻塞等待状态。
+
+2.数据不一致的问题:如果在第二阶段协调者给子系统发送commit指令时,发生了局部网络异常,就会导致接收到commit的指令的子系统提交事务,而没有接收到commit指令的子系统没有提交事务,导致数据不一致。
+
+3.同步阻塞:由于子系统在执行阶段都是同步阻塞的,自身没有超时的机制,一旦与协调者之间的网络断开,只能一直阻塞等待,等待协调者的指令。
+
+#### 3PC三段提交制
+
+这种方案就是分为三个阶段,canCommit,preCommit,doCommit三个阶段。
+
+第一阶段是询问阶段
+
+协调者会给参与者发送canCommit指令,询问能否正常执行,如果满足执行的条件的话,参与者就会返回ACK。
+
+第二阶段是预执行阶段
+
+协调者会给参与者发送preCommit指令,让参与者执行事务,但是执行完成不提交,参与者执行成功后会给协调者发送ACK。
+
+第三阶段就是提交阶段
+
+协调者如果收到所有参与者给他返回执行成功的ACK,那么他就会给所有协调者发送doCommit指令,让参与者提交。如果在第二阶段有一个参与者执行失败,给协调返回执行失败的结果,那么在第三阶段,协调者就会给参与者发送Abort指令,让参与者回滚。
+
+
+
+**与2PC的区别:**
+
+1.3pc是一个非阻塞的协议,为参与者引入了超时机制,在第一阶段或者第二阶段,参与者等待协调者的指令超时了,会进行回滚,第三阶段等待协调者的指令超时了,会进行自动提交。而2pc协议,如果协调者一直没有给参与者发指令,导致超时,参与者会一直进行阻塞等待。
+
+2.2pc协议里面,第二阶段协调者挂了,选举出新的协调者是不知道参与者执行的事务是提交事务还是回滚事务。3pc里面到了第三阶段那么一定是执行提交事务。
+
+**3pc的问题:**
+
+第三阶段如果发送的是abort回滚指令,假设有些参与者由于网络原因没有收到指令,超时后会进行自行提交事务,那么也会导致数据不一致的问题。
+
+https://honeypps.com/architect/introduction-of-distributed-transaction/
+
+https://www.infoq.cn/article/2018/08/rocketmq-4.3-release
+
+#### TCC
+
+TCC分为Try预留阶段,Confirm确认阶段,Cancel撤销阶段三个阶段。
+
+比如某个事务需要A,B,C三个业务系统各自执行一些操作,那么事务管理器会发送Try指令,会让A,B,C三个业务系统各自去申请执行操作所需的一些资源,冻结库存之类的。A,B,C预留资源成功了就会通知事务管理器Try阶段执行成功了。那么事务管理器就会发送Confirm指令给三个业务系统,告诉他们进入到Confirm阶段,让A,B,C业务系统各自执行自己真正的事务操作。如果三个业务系统都执行成功,那么事务管理器就认为执行成功,如果有一个失败那么事务管理器就认为执行失败了,会通知每个业务执行Cancel操作,进行回滚。
+
+(万一某个服务的 Cancel 或者 Confirm 逻辑执行一直失败怎么办呢?
+
+ Cancel 或者 Confirm 一直没成功,会不停的重试调用它的 Cancel 或者 Confirm 逻辑,务必要它成功。)
+
+TCC框架主要有ByteTCC,TCC-transaction,Himly。
+
+缺点:
+
+1.侵入性太强,每个业务系统还需要写Cancel对应的数据回滚相关的逻辑代码。
+
+
+
+
+
+
+
+https://www.cnblogs.com/jajian/p/10014145.html
+
+##### 本地消息表法
+
+就是上游服务先执行操作,将操作记录到数据库中的本地消息表,并且此时这条操作记录的status设置为0,也就是未通知成功。然后将操作记录封装成Kafka消息发送到消息队列,下游系统接受到,进行消费,然后消费成功后调用上游服务的接口,通知他消费成功了,上游系统将本地消息表中这条记录的status设置为1,代表通知成功。
+
+并且上游系统会定时扫描本地消息表,将status为0的操作记录,封装成Kafka消息,发送到消息队列。
+
+并且下游系统是通过消息中操作记录的主键id来防止不重复消费,保证幂等性的。就是消费消息时,发送操作记录的id已经在数据库中存在了,就代表之前已经处理过了,不处理这条消息了。
+
+##### 可靠消息最终一致性方案
+
+RocketMQ在4.3以后,增加了对分布式事务的支持,就是将事务的执行状态保存在RocketMQ中,由RocketMQ去负责将commit状态的消息推送给下游系统。
+
+
+
+1.上游系统发送prepare消息到RocketMQ。
+
+2.prepare消息发送到RocketMQ成功后,上游系统开始执行本地事务。
+
+3.如果上游系统本地事务执行成功,会发送commit消息到RocketMQ,RocketMQ会将这个消息提交,推送给消费者(也就是下游系统)。如果上游系统本地事务执行失败,会发送rollback消息到RocketMQ,RocketMQ会将这个消息撤销,不推送给消费者。
+
+4.如果一个prepare消息一直没有接受到上游系统的commit或者rollback指令,这样就判定prepare消息超时了,RocketMQ会去查询上游系统的这个事务的执行状态,是成功了,还是失败,做下一步的处理。
+
+**底层实现原理**
+
+RocketMQ使用了Half topic队列来保存所有prepare消息,使用Operation Topic队列来保存commit消息和rollback消息。这样通过Operation Topic就知道哪些消息commit了,可以推送给消费者,哪些消息rollback了,不需要推送给消费者。以及那些在Half topic中有,在Operation Topic中没有的消息,就是事务超时的消息。
+
+https://www.infoq.cn/article/2018/08/rocketmq-4.3-release
+
+##### 最大努力通知方案
+
+业务系统 A 执行本地事务完成后,发送个消息到 MQ,有一个个专门消费 MQ 的服务,来消费MQ的消息,消费完会在数据库中记录下来(或者放入到内存队列),之后就一直调用系统 B 的接口,要是系统 B 执行成功就提交,执行失败或者调用超时就一直重试,直到业务系统B执行成功。
+
+### 如何设计秒杀系统?
+
+1.前端页面
+提前把静态资源部署到CDN,减少静态资源访问的压力,其次是可以为静态资源的header设置成强缓存cache-control,2分钟后才过期,这样当第一次请求完静态资源后,再次刷新时,就会使用浏览器里面的缓存的静态资源,而不是再发请求。
+2.nginx层面
+使用limit_req_zone模块针对用户id为key,进行限流,放在同一个用户恶意发起多个请求,限制每个用户每5分钟只能请求100次接口。
+3.业务系统设置限流熔断
+当业务系统收到的请求达到一定限制后,停止接受请求,可以使用hystrix进行限流。如果是秒杀商品,当Redis里面存的商品库存减完时,就对所有用户返回统一的状态码,告诉前端商品被抢光了,不再进行后面的业务逻辑。
+4.使用消息队列削峰
+就是如果秒杀请求对应的业务逻辑处理时间如果过长,例如减库存,生成订单等,业务系统可以先将用户的秒杀请求封装成Kafka消息,发送到消息队列,由其他业务系统消费Kafka消息,完成生成订单等耗时操作。
+https://www.zhihu.com/question/54895548/answer/1352510403
\ No newline at end of file
diff --git a/docs/ZooKeeper.md b/docs/ZooKeeper.md
index 87c3ec0..b916bc3 100644
--- a/docs/ZooKeeper.md
+++ b/docs/ZooKeeper.md
@@ -37,12 +37,18 @@ C得票超过半数,C成为leader,之后加入的D,E也只会投给C。
每个节点都会投给自己,然后选票发给其他节点
##### 2.处理选票信息
从其他节点B收到投票信息后,进行处理
+
2.1 如果本节点的逻辑时钟小于接受这条投票的逻辑时钟,
+
说明本节点之前错过了上一轮的投票,将当前存储的选票信息清空,
+
2.2 如果本节点的逻辑时钟大于接受的这条投票的逻辑时钟,那么忽略掉这条投票信息。
+
2.3 本节点的逻辑时钟等于接受的这条投票的逻辑时钟,那么进行处理,与本节点当前投票的结果进行比较
先比较ZXID(数据ID,越大代表数据越新),ZXID越大的应该当leader,ZXID相同比较SID,SID越大的当leader。
+
2.4如果比较的结果跟当前节点的投票结果不一致,那么需要更改选票,将更改后的选票结果发送给其他节点。
+
2.5 将其他节点B的投票结果记录下来
##### 3.统计选票
@@ -69,9 +75,9 @@ https://blog.csdn.net/hotchange/article/details/81192122
首先客户端连接zookeeper集群中的任何一个节点,可以是leader节点,也可以是follower节点,一旦连接,节点会给客户端分配会话ID,并向客户端发送确认,如果客户端收到确认,那么连接成功,客户端会有规律地给zookeeper发送心跳包,确保连接没有断开。
-* 客户端向zookeeper节点发送读请求,节点会直接从数据库中找到这个节点的数据然后返回。
+* 客户端向zookeeper从节点发送读请求,节点会直接从数据库中找到这个节点的数据然后返回。
-* 客户端向zookeeper节点发送写请求,节点会将znode路径和数据转发到leader节点,leader会将写请求转换为proposal提案,并且分配一个事务ID zxid,将这个proposal放到每个节点的队列(主节点会给每个从节点分配一个专用队列)中去,然后会根据先进先出的策略,将消息发送给从节点,从节点接收到后会将事务写入到磁盘中去,然后返回ACK响应给主节点,当主节点接收到半数以上的从节点的ACK响应后,主节点会认为这个事务提交成功,完成这个事务提交,同时给所有从节点发送commit消息,从节点接收到消息后,会将这条事务提交。
+* 客户端向zookeeper从节点发送写请求,节点会将znode路径和数据转发到leader节点,leader会将写请求转换为proposal提案,并且分配一个事务ID zxid,将这个proposal放到每个节点的队列(主节点会给每个从节点分配一个专用队列)中去,然后会根据先进先出的策略,将消息发送给从节点,从节点接收到后会将事务写入到磁盘中去,然后返回ACK响应给主节点,当主节点接收到半数以上的从节点的ACK响应后,主节点会认为这个事务提交成功,完成这个事务提交,同时给所有从节点发送commit消息,从节点接收到消息后,会将这条事务提交。
由此看来zookeeper没法保证客户端读取的都是最新的数据,
diff --git a/docs/_sidebar.md b/docs/_sidebar.md
index 9a3f431..bd331c5 100644
--- a/docs/_sidebar.md
+++ b/docs/_sidebar.md
@@ -12,17 +12,21 @@
- [持久化(AOF和RDB)](docs/RedisStore.md)
- [高可用(主从切换和哨兵机制)](docs/RedisUserful.md)
* MySQL
- - [原创面试题解答](docs/MySQLNote.md)
+ - [基础](docs/MySQLNote.md)
- [慢查询优化实践](docs/MySQLWork.md)
* JVM
- - [原创面试题解答](docs/JavaJVM.md)
-- [《剑指Offer》解题思考](docs/CodingInterviews.md)
-- [《LeetCode热门100题》解题思考(上)](docs/LeetCode.md)
-- [《LeetCode热门100题》解题思考(下)](docs/LeetCode1.md)
+ - [基础](docs/JavaJVM.md)
- [Kafka](docs/Kafka.md)
- [ZooKeeper](docs/ZooKeeper.md)
- [HTTP](docs/HTTP.md)
-- [大厂面试系列](docs/BATInterview.md)
+- [Spring](docs/Spring.md)
+- [Nginx](docs/Nginx.md)
+- [系统设计](docs/SystemDesign.md)
+* 算法
+ - [《剑指Offer》解题思考](docs/CodingInterviews.md)
+ - [《LeetCode热门100题》解题思考(上)](docs/LeetCode.md)
+ - [《LeetCode热门100题》解题思考(下)](docs/LeetCode1.md)
+- [大厂面试公众号文章系列](docs/BATInterview.md)
* 读书笔记
- [《Redis设计与实现》读书笔记 上](docs/RedisBook1.md)
- [《Redis设计与实现》读书笔记 下](docs/RedisBook2.md)
diff --git a/docs/algorithm.md b/docs/algorithm.md
index afd95b2..eff4a06 100644
--- a/docs/algorithm.md
+++ b/docs/algorithm.md
@@ -49,9 +49,9 @@ int[] sorted(int[] array) {
}
}
// 该趟排序中没有发生,表示已经有序
- if (0 == sortedFlag) {
- break;
- }
+ if (0 == sortedFlag) {
+ break;
+ }
}
return array;
}
@@ -65,14 +65,15 @@ int[] sorted(int[] array) {
int[] sorted2(int[] array) {
if(array == null || array.length==0 || array.length==1) {return array;}
for(int i=1;i0;j--) {
- if(array[j] capacity) {
// 装不下该珠宝
result = testKnapsack1(value,weight,i-1, capacity);
- } else if (weight[i] <= capacity) {//可以选择当前物品的
- // 可以装下
+ } else if (weight[i] <= capacity) {// 可以装下
+ //选择物品i
int choose = testKnapsack1(value,weight,i-1, capacity-weight[i]) + value[i];
+ //不选择物品i
int notChoose = testKnapsack1(value,weight,i-1, capacity);
result = choose > notChoose ? choose : notChoose;
}
@@ -957,3 +989,100 @@ dp[i][0] = Math.max(dp[i-1][0],dp[i-1][1]+prices[i])
return dp[prices.length-1][0];
}
```
+
+
+
+### 421. 数组中两个数的最大异或值
+
+https://leetcode-cn.com/problems/maximum-xor-of-two-numbers-in-an-array/
+
+给定一个非空数组,数组中元素为 a0, a1, a2, … , an-1,其中 0 ≤ ai < 231 。
+
+找到 ai 和aj 最大的异或 (XOR) 运算结果,其中0 ≤ i, j < n 。
+
+你能在O(n)的时间解决这个问题吗?
+
+示例:
+
+输入: [3, 10, 5, 25, 2, 8]
+
+输出: 28
+
+解释: 最大的结果是 5 ^ 25 = 28
+
+
+
+```java
+public static class TreeNode {
+ public int val;
+ public TreeNode left = null;
+ public TreeNode right = null;
+ public TreeNode(int val) {
+ this.val = val;
+ }
+ }
+
+Integer findMaximumXOR(int[] array) {
+ if (array==null||array.length==0) {
+ return null;
+ }
+ TreeNode root = new TreeNode(-1);
+ //构建前缀树
+ for (int i = 0; i < array.length; i++) {
+ insert(root,array[i]);
+ }
+ int max =0;
+
+ for (int i = 0; i < array.length; i++) {
+ int result = findMaxForTheValue(root,array[i]);
+ if (result>max){
+ max= result;
+ }
+ }
+ return max;
+}
+
+void insert(TreeNode root, int insertValue) {
+ //最大值是是2的31次方
+ int bitValue = 1<<30;
+ TreeNode currentNode = root;
+ while (bitValue!=0) {
+ int result = insertValue & bitValue;
+ if (result==0) {//array[i]这一位是0,往左创建节点
+ if (currentNode.left==null) {
+ TreeNode node = new TreeNode(-1);
+ currentNode.left = node;
+ }
+ currentNode = currentNode.left;
+ } else {//array[i]这一位是1,往右边创建节点
+ if (currentNode.right==null) {
+ TreeNode node = new TreeNode(-1);
+ currentNode.right = node;
+ }
+ currentNode = currentNode.right;
+ }
+ bitValue= bitValue>>1;
+ }
+ currentNode.val = insertValue;
+}
+
+int findMaxForTheValue(TreeNode root, int value) {
+ TreeNode currentNode = root;
+ int bitValue = 1<<30;
+ while (bitValue!=0) {
+ int result = value & bitValue;
+ if (result==0) {//array[i]这一位是0,往右边找节点
+
+ currentNode = currentNode.right != null ?
+ currentNode.right : currentNode.left;
+
+ } else {//array[i]这一位是1,往左边找节点
+ currentNode = currentNode.left != null ?
+ currentNode.left : currentNode.right;
+ }
+ bitValue= bitValue>>1;
+ }
+ int result = value^currentNode.val;
+ return result;
+}
+```
\ No newline at end of file
diff --git a/docs/idgenerator.md b/docs/idgenerator.md
index f5b3082..1025af4 100644
--- a/docs/idgenerator.md
+++ b/docs/idgenerator.md
@@ -308,4 +308,31 @@ https://tech.meituan.com/2017/04/21/mt-leaf.html
| **二进制** | 45-47位 | 48-50位 | 51-54位 | 55-57位 | 58-60位 | 61-64位 |
| ---------- | ------- | ------- | ------- | ------- | ------- | ------- |
-| **十进制** | 14位 | 15位 | 16位 | 17位 | 18位 | 19位 |
\ No newline at end of file
+| **十进制** | 14位 | 15位 | 16位 | 17位 | 18位 | 19位 |
+
+
+
+调研了Leaf和uid-generator,由于Leaf有额外的zookeeper依赖,
+
+所以选用了uid-generator,做的改进如下:
+
+1.原本是一旦时钟回拨就抛出异常,修改为时钟回拨小于1s时,就不抛出抛出异常,进行等待1s。
+
+2.加了序列号抛弃策略。按照原有序列号位数分配,是13位,就是每秒可以生成的id数是8192个id,如果并发量比较小,由于每秒获取的id的序列号部分都是从0开始的,或导致后缀0的数据会比较多,容易造成数据倾斜的问题,而且也容易泄露数据信息。可以增加抛弃策略,就是取每一秒的id时,计算一个最大值为序列号的10%随机数,从这个随机数开始取。
+
+3.修改了位数分配,原有的时间位是28位的秒级时间差,最长服务年限只支持8.7年,我们把时间差位数分配了30位,最长可以支持34年。原本机器位是22位,支持启动400万次,我们其实不需要那么多次启动,调整成20位,支持100万次启动。1+30+20+13
+
+4.增加机器位用完时的取余操作,便于复用。
+
+
+
+leaf的缺点:
+
+1.号段模式的信息安全性问题,不考虑机器线下和重启丢掉的这些id,id是完全连续的,容易被竞争对手猜到信息安全性。
+
+2.小问题修复,就是Leaf为了减少对Zookeeper的依赖,在本地也存了一个上次使用的workid的缓存文件,保证在Zookeeper挂掉的情况下,id生成服务能正常启动,读取本地缓存的workid,然后启动。但是本地缓存文件里面只存储了workid,没有存储上次生成的id的时间戳,假如启动前服务器的时间被修改了,那么启动时就没法对时间进行校验,就会导致生成的id重复。
+
+3.缺乏对最大支持年限的检查,时间戳部分溢出会影响符号位,导致生成的id是负数。
+
+4.注册中心只支持Zookeeper,issue里面很多人提出,对于他们的项目来说,由于需要引入Zookeeper依赖,增加部署和维护Zookeeper成本。所以我fork了这个项目,增加了使用MySQL作为注册中心,以及本地配置作为注册中心的模块。
+
diff --git a/docs/linux.md b/docs/linux.md
new file mode 100644
index 0000000..c56e09c
--- /dev/null
+++ b/docs/linux.md
@@ -0,0 +1,40 @@
+### kill pid和kill -9 pid的区别是什么?
+
+kill pid
+
+代表通知应用程序自行关闭。系统会发生一个SIGTERM命令给进程对应的应用程序,让应用程序释放自己的资源后,自行关闭。大部分应用程序可能接受后会释放资源,自行停止,也有一些程序不会理会。
+
+kill -9 pid
+
+代表强制关闭进程。系统会发送一个SIGKILL命令给进程。
+
+一般建议先执行kill pid,然后过一两秒后等程序做一些释放资源的操作,然后再使用kill -9命令强制删除。
+
+https://www.cnblogs.com/aspirant/p/11543456.html
+
+### 僵尸进程和孤儿进程是什么?
+僵尸进程就是子进程调用exit退出或者是运行时发生致命错误,结束运行时,一般会把进程的退出状态通知给操作系统,操作系统发送SIGCHLD信号告诉父进程“子进程退出了”,父进程一般会使用wait系统调用以获得子进程的退出状态,这样内核就可以在内存中释放子进程了,但是如果父进程没有进行wait系统调用,子进程就会驻留在内存,成为僵尸进程。
+
+孤儿进程就是父进程退出,但是它的子进程还在进行,这些子进程就会变成孤儿进程,被init进程(进程号为1)所收养,由它来管理和收集子进程的状态。由于孤儿进程有init进程循环的wait()调用回收资源,所以不会产生什么危害。
+
+##### Linux指令使用
+
+统计access.log中ip访问次数前十的
+
+```
+cat access.log | awk '{ print $1}' | sort -n | uniq -c | sort - r |head 10
+```
+
+统计当前目录下(包含子目录) java 的文件的代码总行数。
+
+```
+wc -l `find . -name "*.java"` | awk '{ sum=sum+$1 } END { print sum }'
+```
+
+### Linux进程间通信的方式?
+
+《Linux 的进程间通信》 https://zhuanlan.zhihu.com/p/58489873
+
+浅析进程间通信的几种方式(含实例源码) https://zhuanlan.zhihu.com/p/94856678
+
+https://mp.weixin.qq.com/s/WgZaS5w5IXa3IBGRsPKtbQ
\ No newline at end of file
diff --git a/index.html b/index.html
index 3adbbbd..a1aa2b7 100644
--- a/index.html
+++ b/index.html
@@ -2,7 +2,7 @@
- 《后端技术总结》
+ 《大厂面试指北》
@@ -14,7 +14,7 @@
-
-
+
+
diff --git a/static/12609483-1829498245a5787b.png b/static/12609483-1829498245a5787b.png
index b1be1a7..3ccd70f 100755
Binary files a/static/12609483-1829498245a5787b.png and b/static/12609483-1829498245a5787b.png differ
diff --git a/static/12609483-199bd1a12bee5cc3.png b/static/12609483-199bd1a12bee5cc3.png
old mode 100644
new mode 100755
index 9e84b0d..546147b
Binary files a/static/12609483-199bd1a12bee5cc3.png and b/static/12609483-199bd1a12bee5cc3.png differ
diff --git a/static/1415794-20190804110605330-45276489.png b/static/1415794-20190804110605330-45276489.png
old mode 100644
new mode 100755
index 028c6ba..b87395d
Binary files a/static/1415794-20190804110605330-45276489.png and b/static/1415794-20190804110605330-45276489.png differ
diff --git a/static/351684196936_pic.jpg b/static/351684196936_pic.jpg
new file mode 100644
index 0000000..434bb31
Binary files /dev/null and b/static/351684196936_pic.jpg differ
diff --git a/static/640 b/static/640
index d50172b..ad20ba4 100644
Binary files a/static/640 and b/static/640 differ
diff --git a/static/640-20210326173442637 b/static/640-20210326173442637
new file mode 100644
index 0000000..d7af96e
Binary files /dev/null and b/static/640-20210326173442637 differ
diff --git a/static/640-20210326191349615 b/static/640-20210326191349615
new file mode 100644
index 0000000..3bfce30
Binary files /dev/null and b/static/640-20210326191349615 differ
diff --git a/static/640-8127488. b/static/640-8127488.
new file mode 100644
index 0000000..3b54fcc
Binary files /dev/null and b/static/640-8127488. differ
diff --git a/static/640-8127813.png b/static/640-8127813.png
new file mode 100644
index 0000000..93498ba
Binary files /dev/null and b/static/640-8127813.png differ
diff --git "a/static/640\347\232\204\345\211\257\346\234\254" "b/static/640\347\232\204\345\211\257\346\234\254"
new file mode 100644
index 0000000..d50172b
Binary files /dev/null and "b/static/640\347\232\204\345\211\257\346\234\254" differ
diff --git a/static/66b6ae1dec5b96084c3a6d29174a20e3.png b/static/66b6ae1dec5b96084c3a6d29174a20e3.png
new file mode 100644
index 0000000..c1a33ae
Binary files /dev/null and b/static/66b6ae1dec5b96084c3a6d29174a20e3.png differ
diff --git a/static/7795953b44734c0c84b94c78943f88ef~tplv-k3u1fbpfcp-zoom-1.image b/static/7795953b44734c0c84b94c78943f88ef~tplv-k3u1fbpfcp-zoom-1.image
deleted file mode 100644
index 807e236..0000000
Binary files a/static/7795953b44734c0c84b94c78943f88ef~tplv-k3u1fbpfcp-zoom-1.image and /dev/null differ
diff --git a/static/WechatIMG0328.jpeg b/static/WechatIMG0328.jpeg
new file mode 100755
index 0000000..e100b6c
Binary files /dev/null and b/static/WechatIMG0328.jpeg differ
diff --git a/static/d7687db8ecbd43a79d041badf07bbaf4~tplv-k3u1fbpfcp-watermark-20210316161015142.image b/static/d7687db8ecbd43a79d041badf07bbaf4~tplv-k3u1fbpfcp-watermark-20210316161015142.image
new file mode 100644
index 0000000..817d252
Binary files /dev/null and b/static/d7687db8ecbd43a79d041badf07bbaf4~tplv-k3u1fbpfcp-watermark-20210316161015142.image differ
diff --git a/static/d7687db8ecbd43a79d041badf07bbaf4~tplv-k3u1fbpfcp-watermark.image b/static/d7687db8ecbd43a79d041badf07bbaf4~tplv-k3u1fbpfcp-watermark.image
new file mode 100644
index 0000000..817d252
Binary files /dev/null and b/static/d7687db8ecbd43a79d041badf07bbaf4~tplv-k3u1fbpfcp-watermark.image differ
diff --git a/static/docsify.min.js b/static/docsify.min.js
index 5e3f1f1..7893b85 100644
--- a/static/docsify.min.js
+++ b/static/docsify.min.js
@@ -1 +1 @@
-!function(){function o(n){var r=Object.create(null);return function(e){var t=c(e)?e:JSON.stringify(e);return r[t]||(r[t]=n(e))}}var i=o(function(e){return e.replace(/([A-Z])/g,function(e){return"-"+e.toLowerCase()})}),l=Object.prototype.hasOwnProperty,d=Object.assign||function(e){for(var t=arguments,n=1;n":">",'"':""","'":"'","/":"/"};return String(e).replace(/[&<>"'/]/g,function(e){return t[e]})}function p(e,t,r,i){void 0===i&&(i=h);var a=e._hooks[t],s=function(t){var e=a[t];if(t>=a.length)i(r);else if("function"==typeof e)if(2===e.length)e(r,function(e){r=e,s(t+1)});else{var n=e(r);r=void 0===n?r:n,s(t+1)}else s(t+1)};s(0)}var g=document.body.clientWidth<=600,a=window.history&&window.history.pushState&&window.history.replaceState&&!navigator.userAgent.match(/((iPod|iPhone|iPad).+\bOS\s+[1-4]\D|WebApps\/.+CFNetwork)/),n={};function m(e,t){if(void 0===t&&(t=!1),"string"==typeof e){if(void 0!==window.Vue)return y(e);e=t?y(e):n[e]||(n[e]=y(e))}return e}var f=document,v=f.body,b=f.head;function y(e,t){return t?e.querySelector(t):f.querySelector(e)}function k(e,t){return[].slice.call(t?e.querySelectorAll(t):f.querySelectorAll(e))}function w(e,t){return e=f.createElement(e),t&&(e.innerHTML=t),e}function s(e,t){return e.appendChild(t)}function x(e,t){return e.insertBefore(t,e.children[0])}function _(e,t,n){r(t)?window.addEventListener(e,t):e.addEventListener(t,n)}function S(e,t,n){r(t)?window.removeEventListener(e,t):e.removeEventListener(t,n)}function A(e,t,n){e&&e.classList[n?t:"toggle"](n||t)}var $,C,e=Object.freeze({__proto__:null,getNode:m,$:f,body:v,head:b,find:y,findAll:k,create:w,appendTo:s,before:x,on:_,off:S,toggleClass:A,style:function(e){s(b,w("style",e))}});function E(e,t){if(void 0===t&&(t=''),!e||!e.length)return"";var n="";return e.forEach(function(e){n+=''+e.title+"",e.children&&(n+=E(e.children,t))}),t.replace("{inner}",n)}function F(e,t){return''+t.slice(5).trim()+"
"}function L(e){var t,n=e.loaded,r=e.total,i=e.step;$||function(){var e=w("div");e.classList.add("progress"),s(v,e),$=e}(),t=i?80<(t=parseInt($.style.width||0,10)+i)?80:t:Math.floor(n/r*100),$.style.opacity=1,$.style.width=95<=t?"100%":t+"%",95<=t&&(clearTimeout(C),C=setTimeout(function(e){$.style.opacity=0,$.style.width="0%"},200))}var T={};function R(a,e,t){void 0===e&&(e=!1),void 0===t&&(t={});function n(){s.addEventListener.apply(s,arguments)}var s=new XMLHttpRequest,r=T[a];if(r)return{then:function(e){return e(r.content,r.opt)},abort:h};for(var i in s.open("GET",a),t)l.call(t,i)&&s.setRequestHeader(i,t[i]);return s.send(),{then:function(r,i){if(void 0===i&&(i=h),e){var t=setInterval(function(e){return L({step:Math.floor(5*Math.random()+1)})},500);n("progress",L),n("loadend",function(e){L(e),clearInterval(t)})}n("error",i),n("load",function(e){var t=e.target;if(400<=t.status)i(t);else{var n=T[a]={content:t.response,opt:{updatedAt:s.getResponseHeader("last-modified")}};r(n.content,n.opt)}})},abort:function(e){return 4!==s.readyState&&s.abort()}}}function O(e,t){e.innerHTML=e.innerHTML.replace(/var\(\s*--theme-color.*?\)/g,t)}var P=/([^{]*?)\w(?=\})/g,z={YYYY:"getFullYear",YY:"getYear",MM:function(e){return e.getMonth()+1},DD:"getDate",HH:"getHours",mm:"getMinutes",ss:"getSeconds",fff:"getMilliseconds"};var t="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function N(e,t){return e(t={exports:{}},t.exports),t.exports}var j=N(function(v,e){!function(){var y={newline:/^\n+/,code:/^( {4}[^\n]+\n*)+/,fences:/^ {0,3}(`{3,}|~{3,})([^`~\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?:\n+|$)|$)/,hr:/^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/,heading:/^ {0,3}(#{1,6}) +([^\n]*?)(?: +#+)? *(?:\n+|$)/,blockquote:/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/,list:/^( {0,3})(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,html:"^ {0,3}(?:<(script|pre|style)[\\s>][\\s\\S]*?(?:\\1>[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?\\?>\\n*|\\n*|\\n*|?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:\\n{2,}|$)|<(?!script|pre|style)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)|(?!script|pre|style)[a-z][\\w-]*\\s*>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$))",def:/^ {0,3}\[(label)\]: *\n? *([^\s>]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/,nptable:h,table:h,lheading:/^([^\n]+)\n {0,3}(=+|-+) *(?:\n+|$)/,_paragraph:/^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html)[^\n]+)*)/,text:/^[^\n]+/};function l(e){this.tokens=[],this.tokens.links=Object.create(null),this.options=e||m.defaults,this.rules=y.normal,this.options.pedantic?this.rules=y.pedantic:this.options.gfm&&(this.rules=y.gfm)}y._label=/(?!\s*\])(?:\\[\[\]]|[^\[\]])+/,y._title=/(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/,y.def=e(y.def).replace("label",y._label).replace("title",y._title).getRegex(),y.bullet=/(?:[*+-]|\d{1,9}\.)/,y.item=/^( *)(bull) ?[^\n]*(?:\n(?!\1bull ?)[^\n]*)*/,y.item=e(y.item,"gm").replace(/bull/g,y.bullet).getRegex(),y.list=e(y.list).replace(/bull/g,y.bullet).replace("hr","\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))").replace("def","\\n+(?="+y.def.source+")").getRegex(),y._tag="address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul",y._comment=//,y.html=e(y.html,"i").replace("comment",y._comment).replace("tag",y._tag).replace("attribute",/ +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(),y.paragraph=e(y._paragraph).replace("hr",y.hr).replace("heading"," {0,3}#{1,6} +").replace("|lheading","").replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}|~{3,})[^`\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html","?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|!--)").replace("tag",y._tag).getRegex(),y.blockquote=e(y.blockquote).replace("paragraph",y.paragraph).getRegex(),y.normal=d({},y),y.gfm=d({},y.normal,{nptable:/^ *([^|\n ].*\|.*)\n *([-:]+ *\|[-| :]*)(?:\n((?:.*[^>\n ].*(?:\n|$))*)\n*|$)/,table:/^ *\|(.+)\n *\|?( *[-:]+[-| :]*)(?:\n((?: *[^>\n ].*(?:\n|$))*)\n*|$)/}),y.pedantic=d({},y.normal,{html:e("^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+?\\1> *(?:\\n{2,}|\\s*$)|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))").replace("comment",y._comment).replace(/tag/g,"(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(),def:/^ *\[([^\]]+)\]: *([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,heading:/^ *(#{1,6}) *([^\n]+?) *(?:#+ *)?(?:\n+|$)/,fences:h,paragraph:e(y.normal._paragraph).replace("hr",y.hr).replace("heading"," *#{1,6} *[^\n]").replace("lheading",y.lheading).replace("blockquote"," {0,3}>").replace("|fences","").replace("|list","").replace("|html","").getRegex()}),l.rules=y,l.lex=function(e,t){return new l(t).lex(e)},l.prototype.lex=function(e){return e=e.replace(/\r\n|\r/g,"\n").replace(/\t/g," ").replace(/\u00a0/g," ").replace(/\u2424/g,"\n"),this.token(e,!0)},l.prototype.token=function(e,t){var n,r,i,a,s,o,l,c,u,h,p,d,g,f,m,v;for(e=e.replace(/^ +$/gm,"");e;)if((i=this.rules.newline.exec(e))&&(e=e.substring(i[0].length),1 ?/gm,""),this.token(i,t),this.tokens.push({type:"blockquote_end"});else if(i=this.rules.list.exec(e)){for(e=e.substring(i[0].length),l={type:"list_start",ordered:f=1<(a=i[2]).length,start:f?+a:"",loose:!1},this.tokens.push(l),n=!(c=[]),g=(i=i[0].match(this.rules.item)).length,p=0;p?@\[\]\\^_`{|}~])/,autolink:/^<(scheme:[^\s\x00-\x1f<>]*|email)>/,url:h,tag:"^comment|^[a-zA-Z][\\w:-]*\\s*>|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^|^",link:/^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/,reflink:/^!?\[(label)\]\[(?!\s*\])((?:\\[\[\]]?|[^\[\]\\])+)\]/,nolink:/^!?\[(?!\s*\])((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\](?:\[\])?/,strong:/^__([^\s_])__(?!_)|^\*\*([^\s*])\*\*(?!\*)|^__([^\s][\s\S]*?[^\s])__(?!_)|^\*\*([^\s][\s\S]*?[^\s])\*\*(?!\*)/,em:/^_([^\s_])_(?!_)|^\*([^\s*<\[])\*(?!\*)|^_([^\s<][\s\S]*?[^\s_])_(?!_|[^\spunctuation])|^_([^\s_<][\s\S]*?[^\s])_(?!_|[^\spunctuation])|^\*([^\s<"][\s\S]*?[^\s\*])\*(?!\*|[^\spunctuation])|^\*([^\s*"<\[][\s\S]*?[^\s])\*(?!\*)/,code:/^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,br:/^( {2,}|\\)\n(?!\s*$)/,del:h,text:/^(`+|[^`])(?:[\s\S]*?(?:(?=[\\?@\\[^_{|}~",n.em=e(n.em).replace(/punctuation/g,n._punctuation).getRegex(),n._escapes=/\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g,n._scheme=/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/,n._email=/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/,n.autolink=e(n.autolink).replace("scheme",n._scheme).replace("email",n._email).getRegex(),n._attribute=/\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/,n.tag=e(n.tag).replace("comment",y._comment).replace("attribute",n._attribute).getRegex(),n._label=/(?:\[[^\[\]]*\]|\\.|`[^`]*`|[^\[\]\\`])*?/,n._href=/<(?:\\[<>]?|[^\s<>\\])*>|[^\s\x00-\x1f]*/,n._title=/"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/,n.link=e(n.link).replace("label",n._label).replace("href",n._href).replace("title",n._title).getRegex(),n.reflink=e(n.reflink).replace("label",n._label).getRegex(),n.normal=d({},n),n.pedantic=d({},n.normal,{strong:/^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,em:/^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/,link:e(/^!?\[(label)\]\((.*?)\)/).replace("label",n._label).getRegex(),reflink:e(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace("label",n._label).getRegex()}),n.gfm=d({},n.normal,{escape:e(n.escape).replace("])","~|])").getRegex(),_extended_email:/[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/,url:/^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/,_backpedal:/(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/,del:/^~+(?=\S)([\s\S]*?\S)~+/,text:/^(`+|[^`])(?:[\s\S]*?(?:(?=[\\/i.test(a[0])&&(this.inLink=!1),!this.inRawBlock&&/^<(pre|code|kbd|script)(\s|>)/i.test(a[0])?this.inRawBlock=!0:this.inRawBlock&&/^<\/(pre|code|kbd|script)(\s|>)/i.test(a[0])&&(this.inRawBlock=!1),e=e.substring(a[0].length),o+=this.options.sanitize?this.options.sanitizer?this.options.sanitizer(a[0]):k(a[0]):a[0];else if(a=this.rules.link.exec(e)){var l=g(a[2],"()");if(-1$/,"$1"),o+=this.outputLink(a,{href:u.escapes(r),title:u.escapes(i)}),this.inLink=!1}else if((a=this.rules.reflink.exec(e))||(a=this.rules.nolink.exec(e))){if(e=e.substring(a[0].length),t=(a[2]||a[1]).replace(/\s+/g," "),!(t=this.links[t.toLowerCase()])||!t.href){o+=a[0].charAt(0),e=a[0].substring(1)+e;continue}this.inLink=!0,o+=this.outputLink(a,t),this.inLink=!1}else if(a=this.rules.strong.exec(e))e=e.substring(a[0].length),o+=this.renderer.strong(this.output(a[4]||a[3]||a[2]||a[1]));else if(a=this.rules.em.exec(e))e=e.substring(a[0].length),o+=this.renderer.em(this.output(a[6]||a[5]||a[4]||a[3]||a[2]||a[1]));else if(a=this.rules.code.exec(e))e=e.substring(a[0].length),o+=this.renderer.codespan(k(a[2].trim(),!0));else if(a=this.rules.br.exec(e))e=e.substring(a[0].length),o+=this.renderer.br();else if(a=this.rules.del.exec(e))e=e.substring(a[0].length),o+=this.renderer.del(this.output(a[1]));else if(a=this.rules.autolink.exec(e))e=e.substring(a[0].length),r="@"===a[2]?"mailto:"+(n=k(this.mangle(a[1]))):n=k(a[1]),o+=this.renderer.link(r,null,n);else if(this.inLink||!(a=this.rules.url.exec(e))){if(a=this.rules.text.exec(e))e=e.substring(a[0].length),this.inRawBlock?o+=this.renderer.text(this.options.sanitize?this.options.sanitizer?this.options.sanitizer(a[0]):k(a[0]):a[0]):o+=this.renderer.text(k(this.smartypants(a[0])));else if(e)throw new Error("Infinite loop on byte: "+e.charCodeAt(0))}else{if("@"===a[2])r="mailto:"+(n=k(a[0]));else{for(;s=a[0],a[0]=this.rules._backpedal.exec(a[0])[0],s!==a[0];);n=k(a[0]),r="www."===a[1]?"http://"+n:n}e=e.substring(a[0].length),o+=this.renderer.link(r,null,n)}return o},u.escapes=function(e){return e?e.replace(u.rules._escapes,"$1"):e},u.prototype.outputLink=function(e,t){var n=t.href,r=t.title?k(t.title):null;return"!"!==e[0].charAt(0)?this.renderer.link(n,r,this.output(e[1])):this.renderer.image(n,r,k(e[1]))},u.prototype.smartypants=function(e){return this.options.smartypants?e.replace(/---/g,"—").replace(/--/g,"–").replace(/(^|[-\u2014/(\[{"\s])'/g,"$1‘").replace(/'/g,"’").replace(/(^|[-\u2014/(\[{\u2018\s])"/g,"$1“").replace(/"/g,"”").replace(/\.{3}/g,"…"):e},u.prototype.mangle=function(e){if(!this.options.mangle)return e;for(var t,n="",r=e.length,i=0;i'+(n?e:k(e,!0))+"\n":""+(n?e:k(e,!0))+"
"},r.prototype.blockquote=function(e){return"\n"+e+"
\n"},r.prototype.html=function(e){return e},r.prototype.heading=function(e,t,n,r){return this.options.headerIds?"\n":""+e+"\n"},r.prototype.hr=function(){return this.options.xhtml?"
\n":"
\n"},r.prototype.list=function(e,t,n){var r=t?"ol":"ul";return"<"+r+(t&&1!==n?' start="'+n+'"':"")+">\n"+e+""+r+">\n"},r.prototype.listitem=function(e){return""+e+"\n"},r.prototype.checkbox=function(e){return" "},r.prototype.paragraph=function(e){return""+e+"
\n"},r.prototype.table=function(e,t){return"\n\n"+e+"\n"+(t=t&&""+t+"")+"
\n"},r.prototype.tablerow=function(e){return"\n"+e+"
\n"},r.prototype.tablecell=function(e,t){var n=t.header?"th":"td";return(t.align?"<"+n+' align="'+t.align+'">':"<"+n+">")+e+""+n+">\n"},r.prototype.strong=function(e){return""+e+""},r.prototype.em=function(e){return""+e+""},r.prototype.codespan=function(e){return""+e+""},r.prototype.br=function(){return this.options.xhtml?"
":"
"},r.prototype.del=function(e){return""+e+""},r.prototype.link=function(e,t,n){if(null===(e=a(this.options.sanitize,this.options.baseUrl,e)))return n;var r='"+n+""},r.prototype.image=function(e,t,n){if(null===(e=a(this.options.sanitize,this.options.baseUrl,e)))return n;var r='
":">"},r.prototype.text=function(e){return e},i.prototype.strong=i.prototype.em=i.prototype.codespan=i.prototype.del=i.prototype.text=function(e){return e},i.prototype.link=i.prototype.image=function(e,t,n){return""+n},i.prototype.br=function(){return""},c.parse=function(e,t){return new c(t).parse(e)},c.prototype.parse=function(e){this.inline=new u(e.links,this.options),this.inlineText=new u(e.links,d({},this.options,{renderer:new i})),this.tokens=e.reverse();for(var t="";this.next();)t+=this.tok();return t},c.prototype.next=function(){return this.token=this.tokens.pop(),this.token},c.prototype.peek=function(){return this.tokens[this.tokens.length-1]||0},c.prototype.parseText=function(){for(var e=this.token.text;"text"===this.peek().type;)e+="\n"+this.next().text;return this.inline.output(e)},c.prototype.tok=function(){switch(this.token.type){case"space":return"";case"hr":return this.renderer.hr();case"heading":return this.renderer.heading(this.inline.output(this.token.text),this.token.depth,p(this.inlineText.output(this.token.text)),this.slugger);case"code":return this.renderer.code(this.token.text,this.token.lang,this.token.escaped);case"table":var e,t,n,r,i="",a="";for(n="",e=0;e?@[\]^`{|}~]/g,"").replace(/\s/g,"-");if(this.seen.hasOwnProperty(t))for(var n=t;this.seen[n]++,t=n+"-"+this.seen[n],this.seen.hasOwnProperty(t););return this.seen[t]=0,t},k.escapeTest=/[&<>"']/,k.escapeReplace=/[&<>"']/g,k.replacements={"&":"&","<":"<",">":">",'"':""","'":"'"},k.escapeTestNoEncode=/[<>"']|&(?!#?\w+;)/,k.escapeReplaceNoEncode=/[<>"']|&(?!#?\w+;)/g;var s={},o=/^$|^[a-z][a-z0-9+.-]*:|^[?#]/i;function h(){}function d(e){for(var t,n,r=arguments,i=1;it)n.splice(t);else for(;n.lengthAn error occurred:
"+k(e.message+"",!0)+"
";throw e}}h.exec=h,m.options=m.setOptions=function(e){return d(m.defaults,e),m},m.getDefaults=function(){return{baseUrl:null,breaks:!1,gfm:!0,headerIds:!0,headerPrefix:"",highlight:null,langPrefix:"language-",mangle:!0,pedantic:!1,renderer:new r,sanitize:!1,sanitizer:null,silent:!1,smartLists:!1,smartypants:!1,xhtml:!1}},m.defaults=m.getDefaults(),m.Parser=c,m.parser=c.parse,m.Renderer=r,m.TextRenderer=i,m.Lexer=l,m.lexer=l.lex,m.InlineLexer=u,m.inlineLexer=u.output,m.Slugger=t,m.parse=m,v.exports=m}()}),M=N(function(e){var c=function(c){var u=/\blang(?:uage)?-([\w-]+)\b/i,t=0,T={manual:c.Prism&&c.Prism.manual,disableWorkerMessageHandler:c.Prism&&c.Prism.disableWorkerMessageHandler,util:{encode:function(e){return e instanceof R?new R(e.type,T.util.encode(e.content),e.alias):Array.isArray(e)?e.map(T.util.encode):e.replace(/&/g,"&").replace(/e.length)return;if(!(y instanceof R)){if(d&&v!=t.length-1){if(u.lastIndex=b,!(A=u.exec(e)))break;for(var k=A.index+(p?A[1].length:0),w=A.index+A[0].length,x=v,_=b,S=t.length;x"+n.content+""+n.tag+">"},!c.document)return c.addEventListener&&(T.disableWorkerMessageHandler||c.addEventListener("message",function(e){var t=JSON.parse(e.data),n=t.language,r=t.code,i=t.immediateClose;c.postMessage(T.highlight(r,T.languages[n],n)),i&&c.close()},!1)),T;var e=document.currentScript||[].slice.call(document.getElementsByTagName("script")).pop();return e&&(T.filename=e.src,T.manual||e.hasAttribute("data-manual")||("loading"!==document.readyState?window.requestAnimationFrame?window.requestAnimationFrame(T.highlightAll):window.setTimeout(T.highlightAll,16):document.addEventListener("DOMContentLoaded",T.highlightAll))),T}("undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{});e.exports&&(e.exports=c),void 0!==t&&(t.Prism=c),c.languages.markup={comment://,prolog:/<\?[\s\S]+?\?>/,doctype://i,cdata://i,tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/i,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/i,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/i,inside:{punctuation:[/^=/,{pattern:/^(\s*)["']|["']$/,lookbehind:!0}]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:/?[\da-z]{1,8};/i},c.languages.markup.tag.inside["attr-value"].inside.entity=c.languages.markup.entity,c.hooks.add("wrap",function(e){"entity"===e.type&&(e.attributes.title=e.content.replace(/&/,"&"))}),Object.defineProperty(c.languages.markup.tag,"addInlined",{value:function(e,t){var n={};n["language-"+t]={pattern:/(^$)/i,lookbehind:!0,inside:c.languages[t]},n.cdata=/^$/i;var r={"included-cdata":{pattern://i,inside:n}};r["language-"+t]={pattern:/[\s\S]+/,inside:c.languages[t]};var i={};i[e]={pattern:RegExp(/(<__[\s\S]*?>)(?:\s*|[\s\S])*?(?=<\/__>)/.source.replace(/__/g,e),"i"),lookbehind:!0,greedy:!0,inside:r},c.languages.insertBefore("markup","cdata",i)}}),c.languages.xml=c.languages.extend("markup",{}),c.languages.html=c.languages.markup,c.languages.mathml=c.languages.markup,c.languages.svg=c.languages.markup,function(e){var t=/("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/;e.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:/@[\w-]+[\s\S]*?(?:;|(?=\s*\{))/,inside:{rule:/@[\w-]+/}},url:{pattern:RegExp("url\\((?:"+t.source+"|[^\n\r()]*)\\)","i"),inside:{function:/^url/i,punctuation:/^\(|\)$/}},selector:RegExp("[^{}\\s](?:[^{};\"']|"+t.source+")*?(?=\\s*\\{)"),string:{pattern:t,greedy:!0},property:/[-_a-z\xA0-\uFFFF][-\w\xA0-\uFFFF]*(?=\s*:)/i,important:/!important\b/i,function:/[-a-z0-9]+(?=\()/i,punctuation:/[(){};:,]/},e.languages.css.atrule.inside.rest=e.languages.css;var n=e.languages.markup;n&&(n.tag.addInlined("style","css"),e.languages.insertBefore("inside","attr-value",{"style-attr":{pattern:/\s*style=("|')(?:\\[\s\S]|(?!\1)[^\\])*\1/i,inside:{"attr-name":{pattern:/^\s*style/i,inside:n.tag.inside},punctuation:/^\s*=\s*['"]|['"]\s*$/,"attr-value":{pattern:/.+/i,inside:e.languages.css}},alias:"language-css"}},n.tag))}(c),c.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/((?:\b(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,boolean:/\b(?:true|false)\b/,function:/\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+\.?\d*|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*|\/|~|\^|%/,punctuation:/[{}[\];(),.:]/},c.languages.javascript=c.languages.extend("clike",{"class-name":[c.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])[_$A-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\.(?:prototype|constructor))/,lookbehind:!0}],keyword:[{pattern:/((?:^|})\s*)(?:catch|finally)\b/,lookbehind:!0},{pattern:/(^|[^.])\b(?:as|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],number:/\b(?:(?:0[xX](?:[\dA-Fa-f](?:_[\dA-Fa-f])?)+|0[bB](?:[01](?:_[01])?)+|0[oO](?:[0-7](?:_[0-7])?)+)n?|(?:\d(?:_\d)?)+n|NaN|Infinity)\b|(?:\b(?:\d(?:_\d)?)+\.?(?:\d(?:_\d)?)*|\B\.(?:\d(?:_\d)?)+)(?:[Ee][+-]?(?:\d(?:_\d)?)+)?/,function:/#?[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,operator:/-[-=]?|\+[+=]?|!=?=?|<=?|>>?>?=?|=(?:==?|>)?|&[&=]?|\|[|=]?|\*\*?=?|\/=?|~|\^=?|%=?|\?|\.{3}/}),c.languages.javascript["class-name"][0].pattern=/(\b(?:class|interface|extends|implements|instanceof|new)\s+)[\w.\\]+/,c.languages.insertBefore("javascript","keyword",{regex:{pattern:/((?:^|[^$\w\xA0-\uFFFF."'\])\s])\s*)\/(\[(?:[^\]\\\r\n]|\\.)*]|\\.|[^/\\\[\r\n])+\/[gimyus]{0,6}(?=\s*($|[\r\n,.;})\]]))/,lookbehind:!0,greedy:!0},"function-variable":{pattern:/#?[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+[_$A-Za-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*)?\s*\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\))/,lookbehind:!0,inside:c.languages.javascript},{pattern:/[_$a-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*=>)/i,inside:c.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\)\s*=>)/,lookbehind:!0,inside:c.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:[_$A-Za-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*\s*)\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\)\s*\{)/,lookbehind:!0,inside:c.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),c.languages.insertBefore("javascript","string",{"template-string":{pattern:/`(?:\\[\s\S]|\${(?:[^{}]|{(?:[^{}]|{[^}]*})*})+}|(?!\${)[^\\`])*`/,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\${(?:[^{}]|{(?:[^{}]|{[^}]*})*})+}/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\${|}$/,alias:"punctuation"},rest:c.languages.javascript}},string:/[\s\S]+/}}}),c.languages.markup&&c.languages.markup.tag.addInlined("script","javascript"),c.languages.js=c.languages.javascript,"undefined"!=typeof self&&self.Prism&&self.document&&document.querySelector&&(self.Prism.fileHighlight=function(e){e=e||document;var l={js:"javascript",py:"python",rb:"ruby",ps1:"powershell",psm1:"powershell",sh:"bash",bat:"batch",h:"c",tex:"latex"};Array.prototype.slice.call(e.querySelectorAll("pre[data-src]")).forEach(function(e){if(!e.hasAttribute("data-src-loaded")){for(var t,n=e.getAttribute("data-src"),r=e,i=/\blang(?:uage)?-([\w-]+)\b/i;r&&!i.test(r.className);)r=r.parentNode;if(r&&(t=(e.className.match(i)||[,""])[1]),!t){var a=(n.match(/\.(\w+)$/)||[,""])[1];t=l[a]||a}var s=document.createElement("code");s.className="language-"+t,e.textContent="",s.textContent="Loading…",e.appendChild(s);var o=new XMLHttpRequest;o.open("GET",n,!0),o.onreadystatechange=function(){4==o.readyState&&(o.status<400&&o.responseText?(s.textContent=o.responseText,c.highlightElement(s),e.setAttribute("data-src-loaded","")):400<=o.status?s.textContent="✖ Error "+o.status+" while fetching file: "+o.statusText:s.textContent="✖ Error: File does not exist or is empty")},o.send(null)}}),c.plugins.toolbar&&c.plugins.toolbar.registerButton("download-file",function(e){var t=e.element.parentNode;if(t&&/pre/i.test(t.nodeName)&&t.hasAttribute("data-src")&&t.hasAttribute("data-download-link")){var n=t.getAttribute("data-src"),r=document.createElement("a");return r.textContent=t.getAttribute("data-download-link-label")||"Download",r.setAttribute("download",""),r.href=n,r}})},document.addEventListener("DOMContentLoaded",function(){self.Prism.fileHighlight()}))});function q(e,r){var i=[],a={};return e.forEach(function(e){var t=e.level||1,n=t-1;r?@[\]^`{|}~]/g;function Z(e){return e.toLowerCase()}function B(e){if("string"!=typeof e)return"";var t=e.trim().replace(/[A-Z]+/g,Z).replace(/<[^>\d]+>/g,"").replace(I,"").replace(/\s/g,"-").replace(/-+/g,"-").replace(/^(\d)/,"_$1"),n=H[t];return n=l.call(H,t)?n+1:0,(H[t]=n)&&(t=t+"-"+n),t}function D(e,t){return'
'}B.clear=function(){H={}};var U=decodeURIComponent,Y=encodeURIComponent;function G(e){var n={};return(e=e.trim().replace(/^(\?|#|&)/,""))&&e.split("&").forEach(function(e){var t=e.replace(/\+/g," ").split("=");n[t[0]]=t[1]&&U(t[1])}),n}function V(e,t){void 0===t&&(t=[]);var n=[];for(var r in e)-1=m.length);n++){var r=t[n];if("string"==typeof r||r.content&&"string"==typeof r.content){var i=m[f],a=d.tokenStack[i],s="string"==typeof r?r:r.content,o=ne(g,i),l=s.indexOf(o);if(-1"}},video:function(e,t){return{html:'"}},audio:function(e,t){return{html:'"}},code:function(e,t){var n=e.match(/\.(\w+)$/);return"md"===(n=t||n&&n[1])&&(n="markdown"),{url:e,lang:n}}},se=function(i,e){var a=this;this.config=i,this.router=e,this.cacheTree={},this.toc=[],this.cacheTOC={},this.linkTarget=i.externalLinkTarget||"_blank",this.linkRel="_blank"===this.linkTarget?i.externalLinkRel||"noopener":"",this.contentBase=e.getBasePath();var s,t=this._initRenderer(),n=i.markdown||{};s=r(n)?n(j,t):(j.setOptions(d(n,{renderer:d(t,n.renderer)})),j),this._marked=s,this.compile=function(n){var r=!0,e=o(function(e){r=!1;var t="";return n?(t=c(n)?s(n):s.parser(n),t=i.noEmoji?t:function(e){return e.replace(/<(pre|template|code)[^>]*?>[\s\S]+?<\/(pre|template|code)>/g,function(e){return e.replace(/:/g,"__colon__")}).replace(/:(\w+?):/gi,window.emojify||D).replace(/__colon__/g,":")}(t),B.clear(),t):n})(n),t=a.router.parse().file;return r?a.toc=a.cacheTOC[t]:a.cacheTOC[t]=[].concat(a.toc),e}};se.prototype.compileEmbed=function(e,t){var n,r=ie(t),i=r.str,a=r.config;if(t=i,a.include){var s;if(W(e)||(e=K(this.contentBase,X(this.router.getCurrentPath()),e)),a.type&&(s=ae[a.type]))(n=s.call(this,e,t)).type=a.type;else{var o="code";/\.(md|markdown)/.test(e)?o="markdown":/\.mmd/.test(e)?o="mermaid":/\.html?/.test(e)?o="iframe":/\.(mp4|ogg)/.test(e)?o="video":/\.mp3/.test(e)&&(o="audio"),(n=ae[o].call(this,e,t)).type=o}return n.fragment=a.fragment,n}},se.prototype._matchNotCompileLink=function(e){for(var t=this.config.noCompileLinks||[],n=0;n'+r+""},t.code=e.code=function(e,t){return void 0===t&&(t=""),e=e.replace(/@DOCSIFY_QM@/g,"`"),''+M.highlight(e,M.languages[t]||M.languages.markup)+"
"},t.link=e.link=function(e,t,n){void 0===t&&(t="");var r="",i=ie(t),a=i.str,s=i.config;return t=a,W(e)||c._matchNotCompileLink(e)||s.ignore?(r+=0===e.indexOf("mailto:")?"":' target="'+o+'"',r+=0===e.indexOf("mailto:")?"":""!==l?' rel="'+l+'"':""):(e===c.config.homepage&&(e="README"),e=u.toURL(e,null,u.getCurrentPath())),s.target&&(r+=" target="+s.target),s.disabled&&(r+=" disabled",e="javascript:void(0)"),t&&(r+=' title="'+t+'"'),'"+n+""},t.paragraph=e.paragraph=function(e){return/^!>/.test(e)?F("tip",e):/^\?>/.test(e)?F("warn",e):""+e+"
"},t.image=e.image=function(e,t,n){var r=e,i="",a=ie(t),s=a.str,o=a.config;t=s,o["no-zoom"]&&(i+=" data-no-zoom"),t&&(i+=' title="'+t+'"');var l=o.size;if(l){var c=l.split("x");c[1]?i+="width="+c[0]+" height="+c[1]:i+="width="+c[0]}return W(e)||(r=K(h,X(u.getCurrentPath()),e)),'
"},t.list=e.list=function(e,t,n){var r=t?"ol":"ul";return"<"+r+" "+[//.test(e.split('class="task-list"')[0])?'class="task-list"':"",n&&1"+e+""+r+">"},t.listitem=e.listitem=function(e){return/^(]*>)/.test(e)?'":""+e+""},e.origin=t,e},se.prototype.sidebar=function(e,t){var n=this.toc,r=this.router.getCurrentPath(),i="";if(e)i=this.compile(e);else{for(var a=0;a{inner}"),this.cacheTree[r]=l}return i},se.prototype.subSidebar=function(e){if(e){var t=this.router.getCurrentPath(),n=this.cacheTree,r=this.toc;r[0]&&r[0].ignoreAllSubs&&r.splice(0),r[0]&&1===r[0].level&&r.shift();for(var i=0;i=t||e.classList.contains("hidden")?A(v,"add","sticky"):A(v,"remove","sticky")}}function ce(e,t,r,n){var i=[];null!=(t=m(t))&&(i=k(t,"a"));var a,s=decodeURI(e.toURL(e.getCurrentPath()));return i.sort(function(e,t){return t.href.length-e.href.length}).forEach(function(e){var t=e.getAttribute("href"),n=r?e.parentNode:e;0!==s.indexOf(t)||a?A(n,"remove","active"):(a=e,A(n,"add","active"))}),n&&(f.title=a?a.title||a.innerText+" - "+oe:oe),a}function ue(e,t){for(var n=0;nthis.end&&e>=this.next}[this.direction]}},{key:"_defaultEase",value:function(e,t,n,r){return(e/=r/2)<1?n/2*e*e+t:-n/2*(--e*(e-2)-1)+t}}]),fe);function fe(){var e=0o){t=t||u;break}t=u}if(t){var h=me[xe(decodeURIComponent(e),t.getAttribute("data-id"))];if(h&&h!==a&&(a&&a.classList.remove("active"),h.classList.add("active"),a=h,!ve&&v.classList.contains("sticky"))){var p=n.clientHeight,d=a.offsetTop+a.clientHeight+40,g=d-0=i.scrollTop&&d<=i.scrollTop+p?i.scrollTop:g?0:d-p;n.scrollTop=f}}}}function xe(e,t){return e+"?id="+t}function _e(e,t){if(t){var n=y("#"+t);n&&function(e){be&&be.stop(),ye=!1,be=new ge({start:window.pageYOffset,end:e.getBoundingClientRect().top+window.pageYOffset,duration:500}).on("tick",function(e){return window.scrollTo(0,e)}).on("done",function(){ye=!0,be=null}).begin()}(n);var r=me[xe(e,t)],i=y(m(".sidebar"),"li.active");i&&i.classList.remove("active"),r&&r.classList.add("active")}}var Se=f.scrollingElement||f.documentElement;var Ae={};function $e(e,i){var s=e.compiler,a=e.raw;void 0===a&&(a="");var t=e.fetch,n=Ae[a];if(n){var r=n.slice();return r.links=n.links,i(r)}var o=s._marked,l=o.lexer(a),c=[],u=o.InlineLexer.rules.link,h=l.links;l.forEach(function(e,a){"paragraph"===e.type&&(e.text=e.text.replace(new RegExp(u.source,"g"),function(e,t,n,r){var i=s.compileEmbed(n,r);return i&&c.push({index:a,embed:i}),e}))});var p=0;!function(e,a){var t,n=e.embedTokens,s=e.compile,o=(e.fetch,0),l=1;if(!n.length)return a({});for(;t=n[o++];){var r=function(i){return function(e){var t;if(e)if("markdown"===i.embed.type)t=s.lexer(e);else if("code"===i.embed.type){if(i.embed.fragment){var n=i.embed.fragment,r=new RegExp("(?:###|\\/\\/\\/)\\s*\\["+n+"\\]([\\s\\S]*)(?:###|\\/\\/\\/)\\s*\\["+n+"\\]");e=((e.match(r)||[])[1]||"").trim()}t=s.lexer("```"+i.embed.lang+"\n"+e.replace(/`/g,"@DOCSIFY_QM@")+"\n```\n")}else"mermaid"===i.embed.type?(t=[{type:"html",text:'
\n'+e+"\n
"}]).links={}:(t=[{type:"html",text:e}]).links={};a({token:i,embedToken:t}),++l>=o&&a({})}}(t);t.embed.url?R(t.embed.url).then(r):r(t.embed.html)}}({compile:o,embedTokens:c,fetch:t},function(e){var t=e.embedToken,n=e.token;if(n){var r=n.index+p;d(h,t.links),l=l.slice(0,r).concat(t,l.slice(r+1)),p+=t.length-1}else Ae[a]=l.concat(),l.links=Ae[a].links=h,i(l)})}function Ce(){var e=k(".markdown-section>script").filter(function(e){return!/template/.test(e.type)})[0];if(!e)return!1;var t=e.innerText.trim();if(!t)return!1;setTimeout(function(e){window.__EXECUTE_RESULT__=new Function(t)()},0)}function Ee(e,t,n){return t="function"==typeof n?n(t):"string"==typeof n?function(r,i){var a=[],s=0;return r.replace(P,function(t,e,n){a.push(r.substring(s,n-1)),s=n+=t.length+1,a.push(i&&i[t]||function(e){return("00"+("string"==typeof z[t]?e[z[t]]():z[t](e))).slice(-t.length)})}),s!==r.length&&a.push(r.substring(s)),function(e){for(var t="",n=0,r=e||new Date;n404 - Not found",this._renderTo(".markdown-section",e),this.config.loadSidebar||this._renderSidebar(),!1===this.config.executeScript||void 0===window.Vue||Ce()?this.config.executeScript&&Ce():setTimeout(function(e){var t=window.__EXECUTE_RESULT__;t&&t.$destroy&&t.$destroy(),window.__EXECUTE_RESULT__=(new window.Vue).$mount("#main")},0)}function Le(e){var t=e.config;e.compiler=new se(t,e.router),window.__current_docsify_compiler__=e.compiler;var n=t.el||"#app",r=y("nav")||w("nav"),i=y(n),a="",s=v;if(i){if(t.repo&&(a+=function(e,t){return e?(/\/\//.test(e)||(e="https://github.com/"+e),''):""}(t.repo,t.cornerExternalLinkTarge)),t.coverpage&&(a+=function(){var e=", 100%, 85%";return''}()),t.logo){var o=/^data:image/.test(t.logo),l=/(?:http[s]?:)?\/\//.test(t.logo),c=/^\./.test(t.logo);o||l||c||(t.logo=K(e.router.getBasePath(),t.logo))}a+=function(e){var t=e.name?u(e.name):"",n='';return(g?n+"":""+n)+''}(t),e._renderTo(i,a,!0)}else e.rendered=!0;t.mergeNavbar&&g?s=y(".sidebar"):(r.classList.add("app-nav"),t.repo||r.classList.add("no-badge")),t.loadNavbar&&x(s,r),t.themeColor&&(f.head.appendChild(w("div",function(e){return""}(t.themeColor)).firstElementChild),function(n){if(!(window.CSS&&window.CSS.supports&&window.CSS.supports("(--v:red)"))){var e=k("style:not(.inserted),link");[].forEach.call(e,function(e){if("STYLE"===e.nodeName)O(e,n);else if("LINK"===e.nodeName){var t=e.getAttribute("href");if(!/\.css$/.test(t))return;R(t).then(function(e){var t=w("style",e);b.appendChild(t),O(t,n)})}})}}(t.themeColor)),e._updateRender(),A(v,"ready")}var Te={};function Re(e){this.config=e}function Oe(e){var t=location.href.indexOf("#");location.replace(location.href.slice(0,0<=t?t:0)+"#"+e)}Re.prototype.getBasePath=function(){return this.config.basePath},Re.prototype.getFile=function(e,t){void 0===e&&(e=this.getCurrentPath());var n=this.config,r=this.getBasePath(),i="string"==typeof n.ext?n.ext:".md";return e=(e=function(e,t){return new RegExp("\\.("+t.replace(/^\./,"")+"|html)$","g").test(e)?e:/\/$/g.test(e)?e+"README"+t:""+e+t}(e=n.alias?function e(t,n,r){var i=Object.keys(n).filter(function(e){return(Te[e]||(Te[e]=new RegExp("^"+e+"$"))).test(t)&&t!==r})[0];return i?e(t.replace(Te[i],n[i]),n,t):t}(e,n.alias):e,i))==="/README"+i&&n.homepage||e,e=W(e)?e:K(r,e),t&&(e=e.replace(new RegExp("^"+r),"")),e},Re.prototype.onchange=function(e){void 0===e&&(e=h),e()},Re.prototype.getCurrentPath=function(){},Re.prototype.normalize=function(){},Re.prototype.parse=function(){},Re.prototype.toURL=function(e,t,n){var r=n&&"#"===e[0],i=this.parse(te(e));if(i.query=d({},i.query,t),e=(e=i.path+V(i.query)).replace(/\.md(\?)|\.md$/,"$1"),r){var a=n.indexOf("?");e=(0([^<]*?)$');if(i){if("color"===i[2])n.style.background=i[1]+(i[3]||"");else{var a=i[1];A(n,"add","has-mask"),W(i[1])||(a=K(this.router.getBasePath(),i[1])),n.style.backgroundImage="url("+a+")",n.style.backgroundSize="cover",n.style.backgroundPosition="center center"}r=r.replace(i[0],"")}this._renderTo(".cover-main",r),le()}else A(n,"remove","show")},Ze._updateRender=function(){!function(e){var t=m(".app-name-link"),n=e.config.nameLink,r=e.route.path;if(t)if(c(e.config.nameLink))t.setAttribute("href",n);else if("object"==typeof n){var i=Object.keys(n).filter(function(e){return-1":">",'"':""","'":"'","/":"/"};return String(e).replace(/[&<>"'/]/g,function(e){return t[e]})}function p(e,t,r,i){void 0===i&&(i=h);var a=e._hooks[t],s=function(t){var e=a[t];if(t>=a.length)i(r);else if("function"==typeof e)if(2===e.length)e(r,function(e){r=e,s(t+1)});else{var n=e(r);r=void 0===n?r:n,s(t+1)}else s(t+1)};s(0)}var g=document.body.clientWidth<=600,a=window.history&&window.history.pushState&&window.history.replaceState&&!navigator.userAgent.match(/((iPod|iPhone|iPad).+\bOS\s+[1-4]\D|WebApps\/.+CFNetwork)/),n={};function m(e,t){if(void 0===t&&(t=!1),"string"==typeof e){if(void 0!==window.Vue)return y(e);e=t?y(e):n[e]||(n[e]=y(e))}return e}var f=document,v=f.body,b=f.head;function y(e,t){return t?e.querySelector(t):f.querySelector(e)}function k(e,t){return[].slice.call(t?e.querySelectorAll(t):f.querySelectorAll(e))}function w(e,t){return e=f.createElement(e),t&&(e.innerHTML=t),e}function s(e,t){return e.appendChild(t)}function x(e,t){return e.insertBefore(t,e.children[0])}function _(e,t,n){r(t)?window.addEventListener(e,t):e.addEventListener(t,n)}function S(e,t,n){r(t)?window.removeEventListener(e,t):e.removeEventListener(t,n)}function A(e,t,n){e&&e.classList[n?t:"toggle"](n||t)}var $,C,e=Object.freeze({__proto__:null,getNode:m,$:f,body:v,head:b,find:y,findAll:k,create:w,appendTo:s,before:x,on:_,off:S,toggleClass:A,style:function(e){s(b,w("style",e))}});function E(e,t){if(void 0===t&&(t=''),!e||!e.length)return"";var n="";return e.forEach(function(e){n+=''+e.title+"",e.children&&(n+=E(e.children,t))}),t.replace("{inner}",n)}function F(e,t){return''+t.slice(5).trim()+"
"}function L(e){var t,n=e.loaded,r=e.total,i=e.step;$||function(){var e=w("div");e.classList.add("progress"),s(v,e),$=e}(),t=i?80<(t=parseInt($.style.width||0,10)+i)?80:t:Math.floor(n/r*100),$.style.opacity=1,$.style.width=95<=t?"100%":t+"%",95<=t&&(clearTimeout(C),C=setTimeout(function(e){$.style.opacity=0,$.style.width="0%"},200))}var T={};function R(a,e,t){void 0===e&&(e=!1),void 0===t&&(t={});function n(){s.addEventListener.apply(s,arguments)}var s=new XMLHttpRequest,r=T[a];if(r)return{then:function(e){return e(r.content,r.opt)},abort:h};for(var i in s.open("GET",a),t)l.call(t,i)&&s.setRequestHeader(i,t[i]);return s.send(),{then:function(r,i){if(void 0===i&&(i=h),e){var t=setInterval(function(e){return L({step:Math.floor(5*Math.random()+1)})},500);n("progress",L),n("loadend",function(e){L(e),clearInterval(t)})}n("error",i),n("load",function(e){var t=e.target;if(400<=t.status)i(t);else{var n=T[a]={content:t.response,opt:{updatedAt:s.getResponseHeader("last-modified")}};r(n.content,n.opt)}})},abort:function(e){return 4!==s.readyState&&s.abort()}}}function O(e,t){e.innerHTML=e.innerHTML.replace(/var\(\s*--theme-color.*?\)/g,t)}var P=/([^{]*?)\w(?=\})/g,z={YYYY:"getFullYear",YY:"getYear",MM:function(e){return e.getMonth()+1},DD:"getDate",HH:"getHours",mm:"getMinutes",ss:"getSeconds",fff:"getMilliseconds"};var t="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function N(e,t){return e(t={exports:{}},t.exports),t.exports}var j=N(function(v,e){!function(){var y={newline:/^\n+/,code:/^( {4}[^\n]+\n*)+/,fences:/^ {0,3}(`{3,}|~{3,})([^`~\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?:\n+|$)|$)/,hr:/^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/,heading:/^ {0,3}(#{1,6}) +([^\n]*?)(?: +#+)? *(?:\n+|$)/,blockquote:/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/,list:/^( {0,3})(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,html:"^ {0,3}(?:<(script|pre|style)[\\s>][\\s\\S]*?(?:\\1>[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?\\?>\\n*|\\n*|\\n*|?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:\\n{2,}|$)|<(?!script|pre|style)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)|(?!script|pre|style)[a-z][\\w-]*\\s*>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$))",def:/^ {0,3}\[(label)\]: *\n? *([^\s>]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/,nptable:h,table:h,lheading:/^([^\n]+)\n {0,3}(=+|-+) *(?:\n+|$)/,_paragraph:/^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html)[^\n]+)*)/,text:/^[^\n]+/};function l(e){this.tokens=[],this.tokens.links=Object.create(null),this.options=e||m.defaults,this.rules=y.normal,this.options.pedantic?this.rules=y.pedantic:this.options.gfm&&(this.rules=y.gfm)}y._label=/(?!\s*\])(?:\\[\[\]]|[^\[\]])+/,y._title=/(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/,y.def=e(y.def).replace("label",y._label).replace("title",y._title).getRegex(),y.bullet=/(?:[*+-]|\d{1,9}\.)/,y.item=/^( *)(bull) ?[^\n]*(?:\n(?!\1bull ?)[^\n]*)*/,y.item=e(y.item,"gm").replace(/bull/g,y.bullet).getRegex(),y.list=e(y.list).replace(/bull/g,y.bullet).replace("hr","\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))").replace("def","\\n+(?="+y.def.source+")").getRegex(),y._tag="address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul",y._comment=//,y.html=e(y.html,"i").replace("comment",y._comment).replace("tag",y._tag).replace("attribute",/ +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(),y.paragraph=e(y._paragraph).replace("hr",y.hr).replace("heading"," {0,3}#{1,6} +").replace("|lheading","").replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}|~{3,})[^`\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html","?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|!--)").replace("tag",y._tag).getRegex(),y.blockquote=e(y.blockquote).replace("paragraph",y.paragraph).getRegex(),y.normal=d({},y),y.gfm=d({},y.normal,{nptable:/^ *([^|\n ].*\|.*)\n *([-:]+ *\|[-| :]*)(?:\n((?:.*[^>\n ].*(?:\n|$))*)\n*|$)/,table:/^ *\|(.+)\n *\|?( *[-:]+[-| :]*)(?:\n((?: *[^>\n ].*(?:\n|$))*)\n*|$)/}),y.pedantic=d({},y.normal,{html:e("^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+?\\1> *(?:\\n{2,}|\\s*$)|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))").replace("comment",y._comment).replace(/tag/g,"(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(),def:/^ *\[([^\]]+)\]: *([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,heading:/^ *(#{1,6}) *([^\n]+?) *(?:#+ *)?(?:\n+|$)/,fences:h,paragraph:e(y.normal._paragraph).replace("hr",y.hr).replace("heading"," *#{1,6} *[^\n]").replace("lheading",y.lheading).replace("blockquote"," {0,3}>").replace("|fences","").replace("|list","").replace("|html","").getRegex()}),l.rules=y,l.lex=function(e,t){return new l(t).lex(e)},l.prototype.lex=function(e){return e=e.replace(/\r\n|\r/g,"\n").replace(/\t/g," ").replace(/\u00a0/g," ").replace(/\u2424/g,"\n"),this.token(e,!0)},l.prototype.token=function(e,t){var n,r,i,a,s,o,l,c,u,h,p,d,g,f,m,v;for(e=e.replace(/^ +$/gm,"");e;)if((i=this.rules.newline.exec(e))&&(e=e.substring(i[0].length),1 ?/gm,""),this.token(i,t),this.tokens.push({type:"blockquote_end"});else if(i=this.rules.list.exec(e)){for(e=e.substring(i[0].length),l={type:"list_start",ordered:f=1<(a=i[2]).length,start:f?+a:"",loose:!1},this.tokens.push(l),n=!(c=[]),g=(i=i[0].match(this.rules.item)).length,p=0;p?@\[\]\\^_`{|}~])/,autolink:/^<(scheme:[^\s\x00-\x1f<>]*|email)>/,url:h,tag:"^comment|^[a-zA-Z][\\w:-]*\\s*>|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^|^",link:/^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/,reflink:/^!?\[(label)\]\[(?!\s*\])((?:\\[\[\]]?|[^\[\]\\])+)\]/,nolink:/^!?\[(?!\s*\])((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\](?:\[\])?/,strong:/^__([^\s_])__(?!_)|^\*\*([^\s*])\*\*(?!\*)|^__([^\s][\s\S]*?[^\s])__(?!_)|^\*\*([^\s][\s\S]*?[^\s])\*\*(?!\*)/,em:/^_([^\s_])_(?!_)|^\*([^\s*<\[])\*(?!\*)|^_([^\s<][\s\S]*?[^\s_])_(?!_|[^\spunctuation])|^_([^\s_<][\s\S]*?[^\s])_(?!_|[^\spunctuation])|^\*([^\s<"][\s\S]*?[^\s\*])\*(?!\*|[^\spunctuation])|^\*([^\s*"<\[][\s\S]*?[^\s])\*(?!\*)/,code:/^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,br:/^( {2,}|\\)\n(?!\s*$)/,del:h,text:/^(`+|[^`])(?:[\s\S]*?(?:(?=[\\?@\\[^_{|}~",n.em=e(n.em).replace(/punctuation/g,n._punctuation).getRegex(),n._escapes=/\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g,n._scheme=/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/,n._email=/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/,n.autolink=e(n.autolink).replace("scheme",n._scheme).replace("email",n._email).getRegex(),n._attribute=/\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/,n.tag=e(n.tag).replace("comment",y._comment).replace("attribute",n._attribute).getRegex(),n._label=/(?:\[[^\[\]]*\]|\\.|`[^`]*`|[^\[\]\\`])*?/,n._href=/<(?:\\[<>]?|[^\s<>\\])*>|[^\s\x00-\x1f]*/,n._title=/"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/,n.link=e(n.link).replace("label",n._label).replace("href",n._href).replace("title",n._title).getRegex(),n.reflink=e(n.reflink).replace("label",n._label).getRegex(),n.normal=d({},n),n.pedantic=d({},n.normal,{strong:/^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,em:/^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/,link:e(/^!?\[(label)\]\((.*?)\)/).replace("label",n._label).getRegex(),reflink:e(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace("label",n._label).getRegex()}),n.gfm=d({},n.normal,{escape:e(n.escape).replace("])","~|])").getRegex(),_extended_email:/[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/,url:/^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/,_backpedal:/(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/,del:/^~+(?=\S)([\s\S]*?\S)~+/,text:/^(`+|[^`])(?:[\s\S]*?(?:(?=[\\/i.test(a[0])&&(this.inLink=!1),!this.inRawBlock&&/^<(pre|code|kbd|script)(\s|>)/i.test(a[0])?this.inRawBlock=!0:this.inRawBlock&&/^<\/(pre|code|kbd|script)(\s|>)/i.test(a[0])&&(this.inRawBlock=!1),e=e.substring(a[0].length),o+=this.options.sanitize?this.options.sanitizer?this.options.sanitizer(a[0]):k(a[0]):a[0];else if(a=this.rules.link.exec(e)){var l=g(a[2],"()");if(-1$/,"$1"),o+=this.outputLink(a,{href:u.escapes(r),title:u.escapes(i)}),this.inLink=!1}else if((a=this.rules.reflink.exec(e))||(a=this.rules.nolink.exec(e))){if(e=e.substring(a[0].length),t=(a[2]||a[1]).replace(/\s+/g," "),!(t=this.links[t.toLowerCase()])||!t.href){o+=a[0].charAt(0),e=a[0].substring(1)+e;continue}this.inLink=!0,o+=this.outputLink(a,t),this.inLink=!1}else if(a=this.rules.strong.exec(e))e=e.substring(a[0].length),o+=this.renderer.strong(this.output(a[4]||a[3]||a[2]||a[1]));else if(a=this.rules.em.exec(e))e=e.substring(a[0].length),o+=this.renderer.em(this.output(a[6]||a[5]||a[4]||a[3]||a[2]||a[1]));else if(a=this.rules.code.exec(e))e=e.substring(a[0].length),o+=this.renderer.codespan(k(a[2].trim(),!0));else if(a=this.rules.br.exec(e))e=e.substring(a[0].length),o+=this.renderer.br();else if(a=this.rules.del.exec(e))e=e.substring(a[0].length),o+=this.renderer.del(this.output(a[1]));else if(a=this.rules.autolink.exec(e))e=e.substring(a[0].length),r="@"===a[2]?"mailto:"+(n=k(this.mangle(a[1]))):n=k(a[1]),o+=this.renderer.link(r,null,n);else if(this.inLink||!(a=this.rules.url.exec(e))){if(a=this.rules.text.exec(e))e=e.substring(a[0].length),this.inRawBlock?o+=this.renderer.text(this.options.sanitize?this.options.sanitizer?this.options.sanitizer(a[0]):k(a[0]):a[0]):o+=this.renderer.text(k(this.smartypants(a[0])));else if(e)throw new Error("Infinite loop on byte: "+e.charCodeAt(0))}else{if("@"===a[2])r="mailto:"+(n=k(a[0]));else{for(;s=a[0],a[0]=this.rules._backpedal.exec(a[0])[0],s!==a[0];);n=k(a[0]),r="www."===a[1]?"http://"+n:n}e=e.substring(a[0].length),o+=this.renderer.link(r,null,n)}return o},u.escapes=function(e){return e?e.replace(u.rules._escapes,"$1"):e},u.prototype.outputLink=function(e,t){var n=t.href,r=t.title?k(t.title):null;return"!"!==e[0].charAt(0)?this.renderer.link(n,r,this.output(e[1])):this.renderer.image(n,r,k(e[1]))},u.prototype.smartypants=function(e){return this.options.smartypants?e.replace(/---/g,"—").replace(/--/g,"–").replace(/(^|[-\u2014/(\[{"\s])'/g,"$1‘").replace(/'/g,"’").replace(/(^|[-\u2014/(\[{\u2018\s])"/g,"$1“").replace(/"/g,"”").replace(/\.{3}/g,"…"):e},u.prototype.mangle=function(e){if(!this.options.mangle)return e;for(var t,n="",r=e.length,i=0;i'+(n?e:k(e,!0))+"\n":""+(n?e:k(e,!0))+"
"},r.prototype.blockquote=function(e){return"\n"+e+"
\n"},r.prototype.html=function(e){return e},r.prototype.heading=function(e,t,n,r){return this.options.headerIds?"\n":""+e+"\n"},r.prototype.hr=function(){return this.options.xhtml?"
\n":"
\n"},r.prototype.list=function(e,t,n){var r=t?"ol":"ul";return"<"+r+(t&&1!==n?' start="'+n+'"':"")+">\n"+e+""+r+">\n"},r.prototype.listitem=function(e){return""+e+"\n"},r.prototype.checkbox=function(e){return" "},r.prototype.paragraph=function(e){return""+e+"
\n"},r.prototype.table=function(e,t){return"\n\n"+e+"\n"+(t=t&&""+t+"")+"
\n"},r.prototype.tablerow=function(e){return"\n"+e+"
\n"},r.prototype.tablecell=function(e,t){var n=t.header?"th":"td";return(t.align?"<"+n+' align="'+t.align+'">':"<"+n+">")+e+""+n+">\n"},r.prototype.strong=function(e){return""+e+""},r.prototype.em=function(e){return""+e+""},r.prototype.codespan=function(e){return""+e+""},r.prototype.br=function(){return this.options.xhtml?"
":"
"},r.prototype.del=function(e){return""+e+""},r.prototype.link=function(e,t,n){if(null===(e=a(this.options.sanitize,this.options.baseUrl,e)))return n;var r='"+n+""},r.prototype.image=function(e,t,n){if(null===(e=a(this.options.sanitize,this.options.baseUrl,e)))return n;var r='
":">"},r.prototype.text=function(e){return e},i.prototype.strong=i.prototype.em=i.prototype.codespan=i.prototype.del=i.prototype.text=function(e){return e},i.prototype.link=i.prototype.image=function(e,t,n){return""+n},i.prototype.br=function(){return""},c.parse=function(e,t){return new c(t).parse(e)},c.prototype.parse=function(e){this.inline=new u(e.links,this.options),this.inlineText=new u(e.links,d({},this.options,{renderer:new i})),this.tokens=e.reverse();for(var t="";this.next();)t+=this.tok();return t},c.prototype.next=function(){return this.token=this.tokens.pop(),this.token},c.prototype.peek=function(){return this.tokens[this.tokens.length-1]||0},c.prototype.parseText=function(){for(var e=this.token.text;"text"===this.peek().type;)e+="\n"+this.next().text;return this.inline.output(e)},c.prototype.tok=function(){switch(this.token.type){case"space":return"";case"hr":return this.renderer.hr();case"heading":return this.renderer.heading(this.inline.output(this.token.text),this.token.depth,p(this.inlineText.output(this.token.text)),this.slugger);case"code":return this.renderer.code(this.token.text,this.token.lang,this.token.escaped);case"table":var e,t,n,r,i="",a="";for(n="",e=0;e?@[\]^`{|}~]/g,"").replace(/\s/g,"-");if(this.seen.hasOwnProperty(t))for(var n=t;this.seen[n]++,t=n+"-"+this.seen[n],this.seen.hasOwnProperty(t););return this.seen[t]=0,t},k.escapeTest=/[&<>"']/,k.escapeReplace=/[&<>"']/g,k.replacements={"&":"&","<":"<",">":">",'"':""","'":"'"},k.escapeTestNoEncode=/[<>"']|&(?!#?\w+;)/,k.escapeReplaceNoEncode=/[<>"']|&(?!#?\w+;)/g;var s={},o=/^$|^[a-z][a-z0-9+.-]*:|^[?#]/i;function h(){}function d(e){for(var t,n,r=arguments,i=1;it)n.splice(t);else for(;n.lengthAn error occurred:"+k(e.message+"",!0)+"
";throw e}}h.exec=h,m.options=m.setOptions=function(e){return d(m.defaults,e),m},m.getDefaults=function(){return{baseUrl:null,breaks:!1,gfm:!0,headerIds:!0,headerPrefix:"",highlight:null,langPrefix:"language-",mangle:!0,pedantic:!1,renderer:new r,sanitize:!1,sanitizer:null,silent:!1,smartLists:!1,smartypants:!1,xhtml:!1}},m.defaults=m.getDefaults(),m.Parser=c,m.parser=c.parse,m.Renderer=r,m.TextRenderer=i,m.Lexer=l,m.lexer=l.lex,m.InlineLexer=u,m.inlineLexer=u.output,m.Slugger=t,m.parse=m,v.exports=m}()}),M=N(function(e){var c=function(c){var u=/\blang(?:uage)?-([\w-]+)\b/i,t=0,T={manual:c.Prism&&c.Prism.manual,disableWorkerMessageHandler:c.Prism&&c.Prism.disableWorkerMessageHandler,util:{encode:function(e){return e instanceof R?new R(e.type,T.util.encode(e.content),e.alias):Array.isArray(e)?e.map(T.util.encode):e.replace(/&/g,"&").replace(/e.length)return;if(!(y instanceof R)){if(d&&v!=t.length-1){if(u.lastIndex=b,!(A=u.exec(e)))break;for(var k=A.index+(p?A[1].length:0),w=A.index+A[0].length,x=v,_=b,S=t.length;x"+n.content+""+n.tag+">"},!c.document)return c.addEventListener&&(T.disableWorkerMessageHandler||c.addEventListener("message",function(e){var t=JSON.parse(e.data),n=t.language,r=t.code,i=t.immediateClose;c.postMessage(T.highlight(r,T.languages[n],n)),i&&c.close()},!1)),T;var e=document.currentScript||[].slice.call(document.getElementsByTagName("script")).pop();return e&&(T.filename=e.src,T.manual||e.hasAttribute("data-manual")||("loading"!==document.readyState?window.requestAnimationFrame?window.requestAnimationFrame(T.highlightAll):window.setTimeout(T.highlightAll,16):document.addEventListener("DOMContentLoaded",T.highlightAll))),T}("undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{});e.exports&&(e.exports=c),void 0!==t&&(t.Prism=c),c.languages.markup={comment://,prolog:/<\?[\s\S]+?\?>/,doctype://i,cdata://i,tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/i,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/i,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/i,inside:{punctuation:[/^=/,{pattern:/^(\s*)["']|["']$/,lookbehind:!0}]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:/?[\da-z]{1,8};/i},c.languages.markup.tag.inside["attr-value"].inside.entity=c.languages.markup.entity,c.hooks.add("wrap",function(e){"entity"===e.type&&(e.attributes.title=e.content.replace(/&/,"&"))}),Object.defineProperty(c.languages.markup.tag,"addInlined",{value:function(e,t){var n={};n["language-"+t]={pattern:/(^$)/i,lookbehind:!0,inside:c.languages[t]},n.cdata=/^$/i;var r={"included-cdata":{pattern://i,inside:n}};r["language-"+t]={pattern:/[\s\S]+/,inside:c.languages[t]};var i={};i[e]={pattern:RegExp(/(<__[\s\S]*?>)(?:\s*|[\s\S])*?(?=<\/__>)/.source.replace(/__/g,e),"i"),lookbehind:!0,greedy:!0,inside:r},c.languages.insertBefore("markup","cdata",i)}}),c.languages.xml=c.languages.extend("markup",{}),c.languages.html=c.languages.markup,c.languages.mathml=c.languages.markup,c.languages.svg=c.languages.markup,function(e){var t=/("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/;e.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:/@[\w-]+[\s\S]*?(?:;|(?=\s*\{))/,inside:{rule:/@[\w-]+/}},url:{pattern:RegExp("url\\((?:"+t.source+"|[^\n\r()]*)\\)","i"),inside:{function:/^url/i,punctuation:/^\(|\)$/}},selector:RegExp("[^{}\\s](?:[^{};\"']|"+t.source+")*?(?=\\s*\\{)"),string:{pattern:t,greedy:!0},property:/[-_a-z\xA0-\uFFFF][-\w\xA0-\uFFFF]*(?=\s*:)/i,important:/!important\b/i,function:/[-a-z0-9]+(?=\()/i,punctuation:/[(){};:,]/},e.languages.css.atrule.inside.rest=e.languages.css;var n=e.languages.markup;n&&(n.tag.addInlined("style","css"),e.languages.insertBefore("inside","attr-value",{"style-attr":{pattern:/\s*style=("|')(?:\\[\s\S]|(?!\1)[^\\])*\1/i,inside:{"attr-name":{pattern:/^\s*style/i,inside:n.tag.inside},punctuation:/^\s*=\s*['"]|['"]\s*$/,"attr-value":{pattern:/.+/i,inside:e.languages.css}},alias:"language-css"}},n.tag))}(c),c.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/((?:\b(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,boolean:/\b(?:true|false)\b/,function:/\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+\.?\d*|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*|\/|~|\^|%/,punctuation:/[{}[\];(),.:]/},c.languages.javascript=c.languages.extend("clike",{"class-name":[c.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])[_$A-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\.(?:prototype|constructor))/,lookbehind:!0}],keyword:[{pattern:/((?:^|})\s*)(?:catch|finally)\b/,lookbehind:!0},{pattern:/(^|[^.])\b(?:as|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],number:/\b(?:(?:0[xX](?:[\dA-Fa-f](?:_[\dA-Fa-f])?)+|0[bB](?:[01](?:_[01])?)+|0[oO](?:[0-7](?:_[0-7])?)+)n?|(?:\d(?:_\d)?)+n|NaN|Infinity)\b|(?:\b(?:\d(?:_\d)?)+\.?(?:\d(?:_\d)?)*|\B\.(?:\d(?:_\d)?)+)(?:[Ee][+-]?(?:\d(?:_\d)?)+)?/,function:/#?[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,operator:/-[-=]?|\+[+=]?|!=?=?|<=?|>>?>?=?|=(?:==?|>)?|&[&=]?|\|[|=]?|\*\*?=?|\/=?|~|\^=?|%=?|\?|\.{3}/}),c.languages.javascript["class-name"][0].pattern=/(\b(?:class|interface|extends|implements|instanceof|new)\s+)[\w.\\]+/,c.languages.insertBefore("javascript","keyword",{regex:{pattern:/((?:^|[^$\w\xA0-\uFFFF."'\])\s])\s*)\/(\[(?:[^\]\\\r\n]|\\.)*]|\\.|[^/\\\[\r\n])+\/[gimyus]{0,6}(?=\s*($|[\r\n,.;})\]]))/,lookbehind:!0,greedy:!0},"function-variable":{pattern:/#?[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+[_$A-Za-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*)?\s*\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\))/,lookbehind:!0,inside:c.languages.javascript},{pattern:/[_$a-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*=>)/i,inside:c.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\)\s*=>)/,lookbehind:!0,inside:c.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:[_$A-Za-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*\s*)\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\)\s*\{)/,lookbehind:!0,inside:c.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),c.languages.insertBefore("javascript","string",{"template-string":{pattern:/`(?:\\[\s\S]|\${(?:[^{}]|{(?:[^{}]|{[^}]*})*})+}|(?!\${)[^\\`])*`/,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\${(?:[^{}]|{(?:[^{}]|{[^}]*})*})+}/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\${|}$/,alias:"punctuation"},rest:c.languages.javascript}},string:/[\s\S]+/}}}),c.languages.markup&&c.languages.markup.tag.addInlined("script","javascript"),c.languages.js=c.languages.javascript,"undefined"!=typeof self&&self.Prism&&self.document&&document.querySelector&&(self.Prism.fileHighlight=function(e){e=e||document;var l={js:"javascript",py:"python",rb:"ruby",ps1:"powershell",psm1:"powershell",sh:"bash",bat:"batch",h:"c",tex:"latex"};Array.prototype.slice.call(e.querySelectorAll("pre[data-src]")).forEach(function(e){if(!e.hasAttribute("data-src-loaded")){for(var t,n=e.getAttribute("data-src"),r=e,i=/\blang(?:uage)?-([\w-]+)\b/i;r&&!i.test(r.className);)r=r.parentNode;if(r&&(t=(e.className.match(i)||[,""])[1]),!t){var a=(n.match(/\.(\w+)$/)||[,""])[1];t=l[a]||a}var s=document.createElement("code");s.className="language-"+t,e.textContent="",s.textContent="Loading…",e.appendChild(s);var o=new XMLHttpRequest;o.open("GET",n,!0),o.onreadystatechange=function(){4==o.readyState&&(o.status<400&&o.responseText?(s.textContent=o.responseText,c.highlightElement(s),e.setAttribute("data-src-loaded","")):400<=o.status?s.textContent="✖ Error "+o.status+" while fetching file: "+o.statusText:s.textContent="✖ Error: File does not exist or is empty")},o.send(null)}}),c.plugins.toolbar&&c.plugins.toolbar.registerButton("download-file",function(e){var t=e.element.parentNode;if(t&&/pre/i.test(t.nodeName)&&t.hasAttribute("data-src")&&t.hasAttribute("data-download-link")){var n=t.getAttribute("data-src"),r=document.createElement("a");return r.textContent=t.getAttribute("data-download-link-label")||"Download",r.setAttribute("download",""),r.href=n,r}})},document.addEventListener("DOMContentLoaded",function(){self.Prism.fileHighlight()}))});function q(e,r){var i=[],a={};return e.forEach(function(e){var t=e.level||1,n=t-1;r?@[\]^`{|}~]/g;function Z(e){return e.toLowerCase()}function B(e){if("string"!=typeof e)return"";var t=e.trim().replace(/[A-Z]+/g,Z).replace(/<[^>\d]+>/g,"").replace(I,"").replace(/\s/g,"-").replace(/-+/g,"-").replace(/^(\d)/,"_$1"),n=H[t];return n=l.call(H,t)?n+1:0,(H[t]=n)&&(t=t+"-"+n),t}function D(e,t){return'
'}B.clear=function(){H={}};var U=decodeURIComponent,Y=encodeURIComponent;function G(e){var n={};return(e=e.trim().replace(/^(\?|#|&)/,""))&&e.split("&").forEach(function(e){var t=e.replace(/\+/g," ").split("=");n[t[0]]=t[1]&&U(t[1])}),n}function V(e,t){void 0===t&&(t=[]);var n=[];for(var r in e)-1=m.length);n++){var r=t[n];if("string"==typeof r||r.content&&"string"==typeof r.content){var i=m[f],a=d.tokenStack[i],s="string"==typeof r?r:r.content,o=ne(g,i),l=s.indexOf(o);if(-1"}},video:function(e,t){return{html:'"}},audio:function(e,t){return{html:'"}},code:function(e,t){var n=e.match(/\.(\w+)$/);return"md"===(n=t||n&&n[1])&&(n="markdown"),{url:e,lang:n}}},se=function(i,e){var a=this;this.config=i,this.router=e,this.cacheTree={},this.toc=[],this.cacheTOC={},this.linkTarget=i.externalLinkTarget||"_blank",this.linkRel="_blank"===this.linkTarget?i.externalLinkRel||"noopener":"",this.contentBase=e.getBasePath();var s,t=this._initRenderer(),n=i.markdown||{};s=r(n)?n(j,t):(j.setOptions(d(n,{renderer:d(t,n.renderer)})),j),this._marked=s,this.compile=function(n){var r=!0,e=o(function(e){r=!1;var t="";return n?(t=c(n)?s(n):s.parser(n),t=i.noEmoji?t:function(e){return e.replace(/<(pre|template|code)[^>]*?>[\s\S]+?<\/(pre|template|code)>/g,function(e){return e.replace(/:/g,"__colon__")}).replace(/:(\w+?):/gi,window.emojify||D).replace(/__colon__/g,":")}(t),B.clear(),t):n})(n),t=a.router.parse().file;return r?a.toc=a.cacheTOC[t]:a.cacheTOC[t]=[].concat(a.toc),e}};se.prototype.compileEmbed=function(e,t){var n,r=ie(t),i=r.str,a=r.config;if(t=i,a.include){var s;if(W(e)||(e=K(this.contentBase,X(this.router.getCurrentPath()),e)),a.type&&(s=ae[a.type]))(n=s.call(this,e,t)).type=a.type;else{var o="code";/\.(md|markdown)/.test(e)?o="markdown":/\.mmd/.test(e)?o="mermaid":/\.html?/.test(e)?o="iframe":/\.(mp4|ogg)/.test(e)?o="video":/\.mp3/.test(e)&&(o="audio"),(n=ae[o].call(this,e,t)).type=o}return n.fragment=a.fragment,n}},se.prototype._matchNotCompileLink=function(e){for(var t=this.config.noCompileLinks||[],n=0;n'+r+""},t.code=e.code=function(e,t){return void 0===t&&(t=""),e=e.replace(/@DOCSIFY_QM@/g,"`"),''+M.highlight(e,M.languages[t]||M.languages.markup)+"
"},t.link=e.link=function(e,t,n){void 0===t&&(t="");var r="",i=ie(t),a=i.str,s=i.config;return t=a,W(e)||c._matchNotCompileLink(e)||s.ignore?(r+=0===e.indexOf("mailto:")?"":' target="'+o+'"',r+=0===e.indexOf("mailto:")?"":""!==l?' rel="'+l+'"':""):(e===c.config.homepage&&(e="README"),e=u.toURL(e,null,u.getCurrentPath())),s.target&&(r+=" target="+s.target),s.disabled&&(r+=" disabled",e="javascript:void(0)"),t&&(r+=' title="'+t+'"'),'"+n+""},t.paragraph=e.paragraph=function(e){return/^!>/.test(e)?F("tip",e):/^\?>/.test(e)?F("warn",e):""+e+"
"},t.image=e.image=function(e,t,n){var r=e,i="",a=ie(t),s=a.str,o=a.config;t=s,o["no-zoom"]&&(i+=" data-no-zoom"),t&&(i+=' title="'+t+'"');var l=o.size;if(l){var c=l.split("x");c[1]?i+="width="+c[0]+" height="+c[1]:i+="width="+c[0]}return W(e)||(r=K(h,X(u.getCurrentPath()),e)),'
"},t.list=e.list=function(e,t,n){var r=t?"ol":"ul";return"<"+r+" "+[//.test(e.split('class="task-list"')[0])?'class="task-list"':"",n&&1"+e+""+r+">"},t.listitem=e.listitem=function(e){return/^(]*>)/.test(e)?'":""+e+""},e.origin=t,e},se.prototype.sidebar=function(e,t){var n=this.toc,r=this.router.getCurrentPath(),i="";if(e)i=this.compile(e);else{for(var a=0;a{inner}"),this.cacheTree[r]=l}return i},se.prototype.subSidebar=function(e){if(e){var t=this.router.getCurrentPath(),n=this.cacheTree,r=this.toc;r[0]&&r[0].ignoreAllSubs&&r.splice(0),r[0]&&1===r[0].level&&r.shift();for(var i=0;i=t||e.classList.contains("hidden")?A(v,"add","sticky"):A(v,"remove","sticky")}}function ce(e,t,r,n){var i=[];null!=(t=m(t))&&(i=k(t,"a"));var a,s=decodeURI(e.toURL(e.getCurrentPath()));return i.sort(function(e,t){return t.href.length-e.href.length}).forEach(function(e){var t=e.getAttribute("href"),n=r?e.parentNode:e;0!==s.indexOf(t)||a?A(n,"remove","active"):(a=e,A(n,"add","active"))}),n&&(f.title=a?a.title||a.innerText+" - "+oe:oe),a}function ue(e,t){for(var n=0;nthis.end&&e>=this.next}[this.direction]}},{key:"_defaultEase",value:function(e,t,n,r){return(e/=r/2)<1?n/2*e*e+t:-n/2*(--e*(e-2)-1)+t}}]),fe);function fe(){var e=0o){t=t||u;break}t=u}if(t){var h=me[xe(decodeURIComponent(e),t.getAttribute("data-id"))];if(h&&h!==a&&(a&&a.classList.remove("active"),h.classList.add("active"),a=h,!ve&&v.classList.contains("sticky"))){var p=n.clientHeight,d=a.offsetTop+a.clientHeight+40,g=d-0=i.scrollTop&&d<=i.scrollTop+p?i.scrollTop:g?0:d-p;n.scrollTop=f}}}}function xe(e,t){return e+"?id="+t}function _e(e,t){if(t){var n=y("#"+t);n&&function(e){be&&be.stop(),ye=!1,be=new ge({start:window.pageYOffset,end:e.getBoundingClientRect().top+window.pageYOffset,duration:500}).on("tick",function(e){return window.scrollTo(0,e)}).on("done",function(){ye=!0,be=null}).begin()}(n);var r=me[xe(e,t)],i=y(m(".sidebar"),"li.active");i&&i.classList.remove("active"),r&&r.classList.add("active")}}var Se=f.scrollingElement||f.documentElement;var Ae={};function $e(e,i){var s=e.compiler,a=e.raw;void 0===a&&(a="");var t=e.fetch,n=Ae[a];if(n){var r=n.slice();return r.links=n.links,i(r)}var o=s._marked,l=o.lexer(a),c=[],u=o.InlineLexer.rules.link,h=l.links;l.forEach(function(e,a){"paragraph"===e.type&&(e.text=e.text.replace(new RegExp(u.source,"g"),function(e,t,n,r){var i=s.compileEmbed(n,r);return i&&c.push({index:a,embed:i}),e}))});var p=0;!function(e,a){var t,n=e.embedTokens,s=e.compile,o=(e.fetch,0),l=1;if(!n.length)return a({});for(;t=n[o++];){var r=function(i){return function(e){var t;if(e)if("markdown"===i.embed.type)t=s.lexer(e);else if("code"===i.embed.type){if(i.embed.fragment){var n=i.embed.fragment,r=new RegExp("(?:###|\\/\\/\\/)\\s*\\["+n+"\\]([\\s\\S]*)(?:###|\\/\\/\\/)\\s*\\["+n+"\\]");e=((e.match(r)||[])[1]||"").trim()}t=s.lexer("```"+i.embed.lang+"\n"+e.replace(/`/g,"@DOCSIFY_QM@")+"\n```\n")}else"mermaid"===i.embed.type?(t=[{type:"html",text:'
\n'+e+"\n
"}]).links={}:(t=[{type:"html",text:e}]).links={};a({token:i,embedToken:t}),++l>=o&&a({})}}(t);t.embed.url?R(t.embed.url).then(r):r(t.embed.html)}}({compile:o,embedTokens:c,fetch:t},function(e){var t=e.embedToken,n=e.token;if(n){var r=n.index+p;d(h,t.links),l=l.slice(0,r).concat(t,l.slice(r+1)),p+=t.length-1}else Ae[a]=l.concat(),l.links=Ae[a].links=h,i(l)})}function Ce(){var e=k(".markdown-section>script").filter(function(e){return!/template/.test(e.type)})[0];if(!e)return!1;var t=e.innerText.trim();if(!t)return!1;setTimeout(function(e){window.__EXECUTE_RESULT__=new Function(t)()},0)}function Ee(e,t,n){return t="function"==typeof n?n(t):"string"==typeof n?function(r,i){var a=[],s=0;return r.replace(P,function(t,e,n){a.push(r.substring(s,n-1)),s=n+=t.length+1,a.push(i&&i[t]||function(e){return("00"+("string"==typeof z[t]?e[z[t]]():z[t](e))).slice(-t.length)})}),s!==r.length&&a.push(r.substring(s)),function(e){for(var t="",n=0,r=e||new Date;n404 - Not found",this._renderTo(".markdown-section",e),this.config.loadSidebar||this._renderSidebar(),!1===this.config.executeScript||void 0===window.Vue||Ce()?this.config.executeScript&&Ce():setTimeout(function(e){var t=window.__EXECUTE_RESULT__;t&&t.$destroy&&t.$destroy(),window.__EXECUTE_RESULT__=(new window.Vue).$mount("#main")},0)}function Le(e){var t=e.config;e.compiler=new se(t,e.router),window.__current_docsify_compiler__=e.compiler;var n=t.el||"#app",r=y("nav")||w("nav"),i=y(n),a="",s=v;if(i){if(t.repo&&(a+=function(e,t){return e?(/\/\//.test(e)||(e="https://github.com/"+e),''):""}(t.repo,t.cornerExternalLinkTarge)),t.coverpage&&(a+=function(){var e=", 100%, 85%";return''}()),t.logo){var o=/^data:image/.test(t.logo),l=/(?:http[s]?:)?\/\//.test(t.logo),c=/^\./.test(t.logo);o||l||c||(t.logo=K(e.router.getBasePath(),t.logo))}a+=function(e){var t=e.name?u(e.name):"",n='';return(g?n+"":""+n)+''}(t),e._renderTo(i,a,!0)}else e.rendered=!0;t.mergeNavbar&&g?s=y(".sidebar"):(r.classList.add("app-nav"),t.repo||r.classList.add("no-badge")),t.loadNavbar&&x(s,r),t.themeColor&&(f.head.appendChild(w("div",function(e){return""}(t.themeColor)).firstElementChild),function(n){if(!(window.CSS&&window.CSS.supports&&window.CSS.supports("(--v:red)"))){var e=k("style:not(.inserted),link");[].forEach.call(e,function(e){if("STYLE"===e.nodeName)O(e,n);else if("LINK"===e.nodeName){var t=e.getAttribute("href");if(!/\.css$/.test(t))return;R(t).then(function(e){var t=w("style",e);b.appendChild(t),O(t,n)})}})}}(t.themeColor)),e._updateRender(),A(v,"ready")}var Te={};function Re(e){this.config=e}function Oe(e){var t=location.href.indexOf("#");location.replace(location.href.slice(0,0<=t?t:0)+"#"+e)}Re.prototype.getBasePath=function(){return this.config.basePath},Re.prototype.getFile=function(e,t){void 0===e&&(e=this.getCurrentPath());var n=this.config,r=this.getBasePath(),i="string"==typeof n.ext?n.ext:".md";return e=(e=function(e,t){return new RegExp("\\.("+t.replace(/^\./,"")+"|html)$","g").test(e)?e:/\/$/g.test(e)?e+"README"+t:""+e+t}(e=n.alias?function e(t,n,r){var i=Object.keys(n).filter(function(e){return(Te[e]||(Te[e]=new RegExp("^"+e+"$"))).test(t)&&t!==r})[0];return i?e(t.replace(Te[i],n[i]),n,t):t}(e,n.alias):e,i))==="/README"+i&&n.homepage||e,e=W(e)?e:K(r,e),t&&(e=e.replace(new RegExp("^"+r),"")),e},Re.prototype.onchange=function(e){void 0===e&&(e=h),e()},Re.prototype.getCurrentPath=function(){},Re.prototype.normalize=function(){},Re.prototype.parse=function(){},Re.prototype.toURL=function(e,t,n){var r=n&&"#"===e[0],i=this.parse(te(e));if(i.query=d({},i.query,t),e=(e=i.path+V(i.query)).replace(/\.md(\?)|\.md$/,"$1"),r){var a=n.indexOf("?");e=(0([^<]*?)$');if(i){if("color"===i[2])n.style.background=i[1]+(i[3]||"");else{var a=i[1];A(n,"add","has-mask"),W(i[1])||(a=K(this.router.getBasePath(),i[1])),n.style.backgroundImage="url("+a+")",n.style.backgroundSize="cover",n.style.backgroundPosition="center center"}r=r.replace(i[0],"")}this._renderTo(".cover-main",r),le()}else A(n,"remove","show")},Ze._updateRender=function(){!function(e){var t=m(".app-name-link"),n=e.config.nameLink,r=e.route.path;if(t)if(c(e.config.nameLink))t.setAttribute("href",n);else if("object"==typeof n){var i=Object.keys(n).filter(function(e){return-1":">",'"':""","'":"'","/":"/"};return String(e).replace(/[&<>"'/]/g,function(e){return t[e]})}function p(e,t,r,i){void 0===i&&(i=h);var a=e._hooks[t],s=function(t){var e=a[t];if(t>=a.length)i(r);else if("function"==typeof e)if(2===e.length)e(r,function(e){r=e,s(t+1)});else{var n=e(r);r=void 0===n?r:n,s(t+1)}else s(t+1)};s(0)}var g=document.body.clientWidth<=600,a=window.history&&window.history.pushState&&window.history.replaceState&&!navigator.userAgent.match(/((iPod|iPhone|iPad).+\bOS\s+[1-4]\D|WebApps\/.+CFNetwork)/),n={};function m(e,t){if(void 0===t&&(t=!1),"string"==typeof e){if(void 0!==window.Vue)return y(e);e=t?y(e):n[e]||(n[e]=y(e))}return e}var f=document,v=f.body,b=f.head;function y(e,t){return t?e.querySelector(t):f.querySelector(e)}function k(e,t){return[].slice.call(t?e.querySelectorAll(t):f.querySelectorAll(e))}function w(e,t){return e=f.createElement(e),t&&(e.innerHTML=t),e}function s(e,t){return e.appendChild(t)}function x(e,t){return e.insertBefore(t,e.children[0])}function _(e,t,n){r(t)?window.addEventListener(e,t):e.addEventListener(t,n)}function S(e,t,n){r(t)?window.removeEventListener(e,t):e.removeEventListener(t,n)}function A(e,t,n){e&&e.classList[n?t:"toggle"](n||t)}var $,C,e=Object.freeze({__proto__:null,getNode:m,$:f,body:v,head:b,find:y,findAll:k,create:w,appendTo:s,before:x,on:_,off:S,toggleClass:A,style:function(e){s(b,w("style",e))}});function E(e,t){if(void 0===t&&(t=''),!e||!e.length)return"";var n="";return e.forEach(function(e){n+=''+e.title+"",e.children&&(n+=E(e.children,t))}),t.replace("{inner}",n)}function F(e,t){return''+t.slice(5).trim()+"
"}function L(e){var t,n=e.loaded,r=e.total,i=e.step;$||function(){var e=w("div");e.classList.add("progress"),s(v,e),$=e}(),t=i?80<(t=parseInt($.style.width||0,10)+i)?80:t:Math.floor(n/r*100),$.style.opacity=1,$.style.width=95<=t?"100%":t+"%",95<=t&&(clearTimeout(C),C=setTimeout(function(e){$.style.opacity=0,$.style.width="0%"},200))}var T={};function R(a,e,t){void 0===e&&(e=!1),void 0===t&&(t={});function n(){s.addEventListener.apply(s,arguments)}var s=new XMLHttpRequest,r=T[a];if(r)return{then:function(e){return e(r.content,r.opt)},abort:h};for(var i in s.open("GET",a),t)l.call(t,i)&&s.setRequestHeader(i,t[i]);return s.send(),{then:function(r,i){if(void 0===i&&(i=h),e){var t=setInterval(function(e){return L({step:Math.floor(5*Math.random()+1)})},500);n("progress",L),n("loadend",function(e){L(e),clearInterval(t)})}n("error",i),n("load",function(e){var t=e.target;if(400<=t.status)i(t);else{var n=T[a]={content:t.response,opt:{updatedAt:s.getResponseHeader("last-modified")}};r(n.content,n.opt)}})},abort:function(e){return 4!==s.readyState&&s.abort()}}}function O(e,t){e.innerHTML=e.innerHTML.replace(/var\(\s*--theme-color.*?\)/g,t)}var P=/([^{]*?)\w(?=\})/g,z={YYYY:"getFullYear",YY:"getYear",MM:function(e){return e.getMonth()+1},DD:"getDate",HH:"getHours",mm:"getMinutes",ss:"getSeconds",fff:"getMilliseconds"};var t="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function N(e,t){return e(t={exports:{}},t.exports),t.exports}var j=N(function(v,e){!function(){var y={newline:/^\n+/,code:/^( {4}[^\n]+\n*)+/,fences:/^ {0,3}(`{3,}|~{3,})([^`~\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?:\n+|$)|$)/,hr:/^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/,heading:/^ {0,3}(#{1,6}) +([^\n]*?)(?: +#+)? *(?:\n+|$)/,blockquote:/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/,list:/^( {0,3})(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,html:"^ {0,3}(?:<(script|pre|style)[\\s>][\\s\\S]*?(?:\\1>[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?\\?>\\n*|\\n*|\\n*|?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:\\n{2,}|$)|<(?!script|pre|style)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)|(?!script|pre|style)[a-z][\\w-]*\\s*>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$))",def:/^ {0,3}\[(label)\]: *\n? *([^\s>]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/,nptable:h,table:h,lheading:/^([^\n]+)\n {0,3}(=+|-+) *(?:\n+|$)/,_paragraph:/^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html)[^\n]+)*)/,text:/^[^\n]+/};function l(e){this.tokens=[],this.tokens.links=Object.create(null),this.options=e||m.defaults,this.rules=y.normal,this.options.pedantic?this.rules=y.pedantic:this.options.gfm&&(this.rules=y.gfm)}y._label=/(?!\s*\])(?:\\[\[\]]|[^\[\]])+/,y._title=/(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/,y.def=e(y.def).replace("label",y._label).replace("title",y._title).getRegex(),y.bullet=/(?:[*+-]|\d{1,9}\.)/,y.item=/^( *)(bull) ?[^\n]*(?:\n(?!\1bull ?)[^\n]*)*/,y.item=e(y.item,"gm").replace(/bull/g,y.bullet).getRegex(),y.list=e(y.list).replace(/bull/g,y.bullet).replace("hr","\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))").replace("def","\\n+(?="+y.def.source+")").getRegex(),y._tag="address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul",y._comment=//,y.html=e(y.html,"i").replace("comment",y._comment).replace("tag",y._tag).replace("attribute",/ +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(),y.paragraph=e(y._paragraph).replace("hr",y.hr).replace("heading"," {0,3}#{1,6} +").replace("|lheading","").replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}|~{3,})[^`\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html","?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|!--)").replace("tag",y._tag).getRegex(),y.blockquote=e(y.blockquote).replace("paragraph",y.paragraph).getRegex(),y.normal=d({},y),y.gfm=d({},y.normal,{nptable:/^ *([^|\n ].*\|.*)\n *([-:]+ *\|[-| :]*)(?:\n((?:.*[^>\n ].*(?:\n|$))*)\n*|$)/,table:/^ *\|(.+)\n *\|?( *[-:]+[-| :]*)(?:\n((?: *[^>\n ].*(?:\n|$))*)\n*|$)/}),y.pedantic=d({},y.normal,{html:e("^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+?\\1> *(?:\\n{2,}|\\s*$)|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))").replace("comment",y._comment).replace(/tag/g,"(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(),def:/^ *\[([^\]]+)\]: *([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,heading:/^ *(#{1,6}) *([^\n]+?) *(?:#+ *)?(?:\n+|$)/,fences:h,paragraph:e(y.normal._paragraph).replace("hr",y.hr).replace("heading"," *#{1,6} *[^\n]").replace("lheading",y.lheading).replace("blockquote"," {0,3}>").replace("|fences","").replace("|list","").replace("|html","").getRegex()}),l.rules=y,l.lex=function(e,t){return new l(t).lex(e)},l.prototype.lex=function(e){return e=e.replace(/\r\n|\r/g,"\n").replace(/\t/g," ").replace(/\u00a0/g," ").replace(/\u2424/g,"\n"),this.token(e,!0)},l.prototype.token=function(e,t){var n,r,i,a,s,o,l,c,u,h,p,d,g,f,m,v;for(e=e.replace(/^ +$/gm,"");e;)if((i=this.rules.newline.exec(e))&&(e=e.substring(i[0].length),1 ?/gm,""),this.token(i,t),this.tokens.push({type:"blockquote_end"});else if(i=this.rules.list.exec(e)){for(e=e.substring(i[0].length),l={type:"list_start",ordered:f=1<(a=i[2]).length,start:f?+a:"",loose:!1},this.tokens.push(l),n=!(c=[]),g=(i=i[0].match(this.rules.item)).length,p=0;p?@\[\]\\^_`{|}~])/,autolink:/^<(scheme:[^\s\x00-\x1f<>]*|email)>/,url:h,tag:"^comment|^[a-zA-Z][\\w:-]*\\s*>|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^|^",link:/^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/,reflink:/^!?\[(label)\]\[(?!\s*\])((?:\\[\[\]]?|[^\[\]\\])+)\]/,nolink:/^!?\[(?!\s*\])((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\](?:\[\])?/,strong:/^__([^\s_])__(?!_)|^\*\*([^\s*])\*\*(?!\*)|^__([^\s][\s\S]*?[^\s])__(?!_)|^\*\*([^\s][\s\S]*?[^\s])\*\*(?!\*)/,em:/^_([^\s_])_(?!_)|^\*([^\s*<\[])\*(?!\*)|^_([^\s<][\s\S]*?[^\s_])_(?!_|[^\spunctuation])|^_([^\s_<][\s\S]*?[^\s])_(?!_|[^\spunctuation])|^\*([^\s<"][\s\S]*?[^\s\*])\*(?!\*|[^\spunctuation])|^\*([^\s*"<\[][\s\S]*?[^\s])\*(?!\*)/,code:/^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,br:/^( {2,}|\\)\n(?!\s*$)/,del:h,text:/^(`+|[^`])(?:[\s\S]*?(?:(?=[\\?@\\[^_{|}~",n.em=e(n.em).replace(/punctuation/g,n._punctuation).getRegex(),n._escapes=/\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g,n._scheme=/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/,n._email=/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/,n.autolink=e(n.autolink).replace("scheme",n._scheme).replace("email",n._email).getRegex(),n._attribute=/\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/,n.tag=e(n.tag).replace("comment",y._comment).replace("attribute",n._attribute).getRegex(),n._label=/(?:\[[^\[\]]*\]|\\.|`[^`]*`|[^\[\]\\`])*?/,n._href=/<(?:\\[<>]?|[^\s<>\\])*>|[^\s\x00-\x1f]*/,n._title=/"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/,n.link=e(n.link).replace("label",n._label).replace("href",n._href).replace("title",n._title).getRegex(),n.reflink=e(n.reflink).replace("label",n._label).getRegex(),n.normal=d({},n),n.pedantic=d({},n.normal,{strong:/^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,em:/^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/,link:e(/^!?\[(label)\]\((.*?)\)/).replace("label",n._label).getRegex(),reflink:e(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace("label",n._label).getRegex()}),n.gfm=d({},n.normal,{escape:e(n.escape).replace("])","~|])").getRegex(),_extended_email:/[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/,url:/^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/,_backpedal:/(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/,del:/^~+(?=\S)([\s\S]*?\S)~+/,text:/^(`+|[^`])(?:[\s\S]*?(?:(?=[\\/i.test(a[0])&&(this.inLink=!1),!this.inRawBlock&&/^<(pre|code|kbd|script)(\s|>)/i.test(a[0])?this.inRawBlock=!0:this.inRawBlock&&/^<\/(pre|code|kbd|script)(\s|>)/i.test(a[0])&&(this.inRawBlock=!1),e=e.substring(a[0].length),o+=this.options.sanitize?this.options.sanitizer?this.options.sanitizer(a[0]):k(a[0]):a[0];else if(a=this.rules.link.exec(e)){var l=g(a[2],"()");if(-1$/,"$1"),o+=this.outputLink(a,{href:u.escapes(r),title:u.escapes(i)}),this.inLink=!1}else if((a=this.rules.reflink.exec(e))||(a=this.rules.nolink.exec(e))){if(e=e.substring(a[0].length),t=(a[2]||a[1]).replace(/\s+/g," "),!(t=this.links[t.toLowerCase()])||!t.href){o+=a[0].charAt(0),e=a[0].substring(1)+e;continue}this.inLink=!0,o+=this.outputLink(a,t),this.inLink=!1}else if(a=this.rules.strong.exec(e))e=e.substring(a[0].length),o+=this.renderer.strong(this.output(a[4]||a[3]||a[2]||a[1]));else if(a=this.rules.em.exec(e))e=e.substring(a[0].length),o+=this.renderer.em(this.output(a[6]||a[5]||a[4]||a[3]||a[2]||a[1]));else if(a=this.rules.code.exec(e))e=e.substring(a[0].length),o+=this.renderer.codespan(k(a[2].trim(),!0));else if(a=this.rules.br.exec(e))e=e.substring(a[0].length),o+=this.renderer.br();else if(a=this.rules.del.exec(e))e=e.substring(a[0].length),o+=this.renderer.del(this.output(a[1]));else if(a=this.rules.autolink.exec(e))e=e.substring(a[0].length),r="@"===a[2]?"mailto:"+(n=k(this.mangle(a[1]))):n=k(a[1]),o+=this.renderer.link(r,null,n);else if(this.inLink||!(a=this.rules.url.exec(e))){if(a=this.rules.text.exec(e))e=e.substring(a[0].length),this.inRawBlock?o+=this.renderer.text(this.options.sanitize?this.options.sanitizer?this.options.sanitizer(a[0]):k(a[0]):a[0]):o+=this.renderer.text(k(this.smartypants(a[0])));else if(e)throw new Error("Infinite loop on byte: "+e.charCodeAt(0))}else{if("@"===a[2])r="mailto:"+(n=k(a[0]));else{for(;s=a[0],a[0]=this.rules._backpedal.exec(a[0])[0],s!==a[0];);n=k(a[0]),r="www."===a[1]?"http://"+n:n}e=e.substring(a[0].length),o+=this.renderer.link(r,null,n)}return o},u.escapes=function(e){return e?e.replace(u.rules._escapes,"$1"):e},u.prototype.outputLink=function(e,t){var n=t.href,r=t.title?k(t.title):null;return"!"!==e[0].charAt(0)?this.renderer.link(n,r,this.output(e[1])):this.renderer.image(n,r,k(e[1]))},u.prototype.smartypants=function(e){return this.options.smartypants?e.replace(/---/g,"—").replace(/--/g,"–").replace(/(^|[-\u2014/(\[{"\s])'/g,"$1‘").replace(/'/g,"’").replace(/(^|[-\u2014/(\[{\u2018\s])"/g,"$1“").replace(/"/g,"”").replace(/\.{3}/g,"…"):e},u.prototype.mangle=function(e){if(!this.options.mangle)return e;for(var t,n="",r=e.length,i=0;i'+(n?e:k(e,!0))+"\n":""+(n?e:k(e,!0))+"
"},r.prototype.blockquote=function(e){return"\n"+e+"
\n"},r.prototype.html=function(e){return e},r.prototype.heading=function(e,t,n,r){return this.options.headerIds?"\n":""+e+"\n"},r.prototype.hr=function(){return this.options.xhtml?"
\n":"
\n"},r.prototype.list=function(e,t,n){var r=t?"ol":"ul";return"<"+r+(t&&1!==n?' start="'+n+'"':"")+">\n"+e+""+r+">\n"},r.prototype.listitem=function(e){return""+e+"\n"},r.prototype.checkbox=function(e){return" "},r.prototype.paragraph=function(e){return""+e+"
\n"},r.prototype.table=function(e,t){return"\n\n"+e+"\n"+(t=t&&""+t+"")+"
\n"},r.prototype.tablerow=function(e){return"\n"+e+"
\n"},r.prototype.tablecell=function(e,t){var n=t.header?"th":"td";return(t.align?"<"+n+' align="'+t.align+'">':"<"+n+">")+e+""+n+">\n"},r.prototype.strong=function(e){return""+e+""},r.prototype.em=function(e){return""+e+""},r.prototype.codespan=function(e){return""+e+""},r.prototype.br=function(){return this.options.xhtml?"
":"
"},r.prototype.del=function(e){return""+e+""},r.prototype.link=function(e,t,n){if(null===(e=a(this.options.sanitize,this.options.baseUrl,e)))return n;var r='"+n+""},r.prototype.image=function(e,t,n){if(null===(e=a(this.options.sanitize,this.options.baseUrl,e)))return n;var r='
":">"},r.prototype.text=function(e){return e},i.prototype.strong=i.prototype.em=i.prototype.codespan=i.prototype.del=i.prototype.text=function(e){return e},i.prototype.link=i.prototype.image=function(e,t,n){return""+n},i.prototype.br=function(){return""},c.parse=function(e,t){return new c(t).parse(e)},c.prototype.parse=function(e){this.inline=new u(e.links,this.options),this.inlineText=new u(e.links,d({},this.options,{renderer:new i})),this.tokens=e.reverse();for(var t="";this.next();)t+=this.tok();return t},c.prototype.next=function(){return this.token=this.tokens.pop(),this.token},c.prototype.peek=function(){return this.tokens[this.tokens.length-1]||0},c.prototype.parseText=function(){for(var e=this.token.text;"text"===this.peek().type;)e+="\n"+this.next().text;return this.inline.output(e)},c.prototype.tok=function(){switch(this.token.type){case"space":return"";case"hr":return this.renderer.hr();case"heading":return this.renderer.heading(this.inline.output(this.token.text),this.token.depth,p(this.inlineText.output(this.token.text)),this.slugger);case"code":return this.renderer.code(this.token.text,this.token.lang,this.token.escaped);case"table":var e,t,n,r,i="",a="";for(n="",e=0;e?@[\]^`{|}~]/g,"").replace(/\s/g,"-");if(this.seen.hasOwnProperty(t))for(var n=t;this.seen[n]++,t=n+"-"+this.seen[n],this.seen.hasOwnProperty(t););return this.seen[t]=0,t},k.escapeTest=/[&<>"']/,k.escapeReplace=/[&<>"']/g,k.replacements={"&":"&","<":"<",">":">",'"':""","'":"'"},k.escapeTestNoEncode=/[<>"']|&(?!#?\w+;)/,k.escapeReplaceNoEncode=/[<>"']|&(?!#?\w+;)/g;var s={},o=/^$|^[a-z][a-z0-9+.-]*:|^[?#]/i;function h(){}function d(e){for(var t,n,r=arguments,i=1;it)n.splice(t);else for(;n.lengthAn error occurred:"+k(e.message+"",!0)+"
";throw e}}h.exec=h,m.options=m.setOptions=function(e){return d(m.defaults,e),m},m.getDefaults=function(){return{baseUrl:null,breaks:!1,gfm:!0,headerIds:!0,headerPrefix:"",highlight:null,langPrefix:"language-",mangle:!0,pedantic:!1,renderer:new r,sanitize:!1,sanitizer:null,silent:!1,smartLists:!1,smartypants:!1,xhtml:!1}},m.defaults=m.getDefaults(),m.Parser=c,m.parser=c.parse,m.Renderer=r,m.TextRenderer=i,m.Lexer=l,m.lexer=l.lex,m.InlineLexer=u,m.inlineLexer=u.output,m.Slugger=t,m.parse=m,v.exports=m}()}),M=N(function(e){var c=function(c){var u=/\blang(?:uage)?-([\w-]+)\b/i,t=0,T={manual:c.Prism&&c.Prism.manual,disableWorkerMessageHandler:c.Prism&&c.Prism.disableWorkerMessageHandler,util:{encode:function(e){return e instanceof R?new R(e.type,T.util.encode(e.content),e.alias):Array.isArray(e)?e.map(T.util.encode):e.replace(/&/g,"&").replace(/e.length)return;if(!(y instanceof R)){if(d&&v!=t.length-1){if(u.lastIndex=b,!(A=u.exec(e)))break;for(var k=A.index+(p?A[1].length:0),w=A.index+A[0].length,x=v,_=b,S=t.length;x"+n.content+""+n.tag+">"},!c.document)return c.addEventListener&&(T.disableWorkerMessageHandler||c.addEventListener("message",function(e){var t=JSON.parse(e.data),n=t.language,r=t.code,i=t.immediateClose;c.postMessage(T.highlight(r,T.languages[n],n)),i&&c.close()},!1)),T;var e=document.currentScript||[].slice.call(document.getElementsByTagName("script")).pop();return e&&(T.filename=e.src,T.manual||e.hasAttribute("data-manual")||("loading"!==document.readyState?window.requestAnimationFrame?window.requestAnimationFrame(T.highlightAll):window.setTimeout(T.highlightAll,16):document.addEventListener("DOMContentLoaded",T.highlightAll))),T}("undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{});e.exports&&(e.exports=c),void 0!==t&&(t.Prism=c),c.languages.markup={comment://,prolog:/<\?[\s\S]+?\?>/,doctype://i,cdata://i,tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/i,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/i,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/i,inside:{punctuation:[/^=/,{pattern:/^(\s*)["']|["']$/,lookbehind:!0}]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:/?[\da-z]{1,8};/i},c.languages.markup.tag.inside["attr-value"].inside.entity=c.languages.markup.entity,c.hooks.add("wrap",function(e){"entity"===e.type&&(e.attributes.title=e.content.replace(/&/,"&"))}),Object.defineProperty(c.languages.markup.tag,"addInlined",{value:function(e,t){var n={};n["language-"+t]={pattern:/(^$)/i,lookbehind:!0,inside:c.languages[t]},n.cdata=/^$/i;var r={"included-cdata":{pattern://i,inside:n}};r["language-"+t]={pattern:/[\s\S]+/,inside:c.languages[t]};var i={};i[e]={pattern:RegExp(/(<__[\s\S]*?>)(?:\s*|[\s\S])*?(?=<\/__>)/.source.replace(/__/g,e),"i"),lookbehind:!0,greedy:!0,inside:r},c.languages.insertBefore("markup","cdata",i)}}),c.languages.xml=c.languages.extend("markup",{}),c.languages.html=c.languages.markup,c.languages.mathml=c.languages.markup,c.languages.svg=c.languages.markup,function(e){var t=/("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/;e.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:/@[\w-]+[\s\S]*?(?:;|(?=\s*\{))/,inside:{rule:/@[\w-]+/}},url:{pattern:RegExp("url\\((?:"+t.source+"|[^\n\r()]*)\\)","i"),inside:{function:/^url/i,punctuation:/^\(|\)$/}},selector:RegExp("[^{}\\s](?:[^{};\"']|"+t.source+")*?(?=\\s*\\{)"),string:{pattern:t,greedy:!0},property:/[-_a-z\xA0-\uFFFF][-\w\xA0-\uFFFF]*(?=\s*:)/i,important:/!important\b/i,function:/[-a-z0-9]+(?=\()/i,punctuation:/[(){};:,]/},e.languages.css.atrule.inside.rest=e.languages.css;var n=e.languages.markup;n&&(n.tag.addInlined("style","css"),e.languages.insertBefore("inside","attr-value",{"style-attr":{pattern:/\s*style=("|')(?:\\[\s\S]|(?!\1)[^\\])*\1/i,inside:{"attr-name":{pattern:/^\s*style/i,inside:n.tag.inside},punctuation:/^\s*=\s*['"]|['"]\s*$/,"attr-value":{pattern:/.+/i,inside:e.languages.css}},alias:"language-css"}},n.tag))}(c),c.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/((?:\b(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,boolean:/\b(?:true|false)\b/,function:/\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+\.?\d*|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*|\/|~|\^|%/,punctuation:/[{}[\];(),.:]/},c.languages.javascript=c.languages.extend("clike",{"class-name":[c.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])[_$A-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\.(?:prototype|constructor))/,lookbehind:!0}],keyword:[{pattern:/((?:^|})\s*)(?:catch|finally)\b/,lookbehind:!0},{pattern:/(^|[^.])\b(?:as|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],number:/\b(?:(?:0[xX](?:[\dA-Fa-f](?:_[\dA-Fa-f])?)+|0[bB](?:[01](?:_[01])?)+|0[oO](?:[0-7](?:_[0-7])?)+)n?|(?:\d(?:_\d)?)+n|NaN|Infinity)\b|(?:\b(?:\d(?:_\d)?)+\.?(?:\d(?:_\d)?)*|\B\.(?:\d(?:_\d)?)+)(?:[Ee][+-]?(?:\d(?:_\d)?)+)?/,function:/#?[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,operator:/-[-=]?|\+[+=]?|!=?=?|<=?|>>?>?=?|=(?:==?|>)?|&[&=]?|\|[|=]?|\*\*?=?|\/=?|~|\^=?|%=?|\?|\.{3}/}),c.languages.javascript["class-name"][0].pattern=/(\b(?:class|interface|extends|implements|instanceof|new)\s+)[\w.\\]+/,c.languages.insertBefore("javascript","keyword",{regex:{pattern:/((?:^|[^$\w\xA0-\uFFFF."'\])\s])\s*)\/(\[(?:[^\]\\\r\n]|\\.)*]|\\.|[^/\\\[\r\n])+\/[gimyus]{0,6}(?=\s*($|[\r\n,.;})\]]))/,lookbehind:!0,greedy:!0},"function-variable":{pattern:/#?[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+[_$A-Za-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*)?\s*\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\))/,lookbehind:!0,inside:c.languages.javascript},{pattern:/[_$a-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*=>)/i,inside:c.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\)\s*=>)/,lookbehind:!0,inside:c.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:[_$A-Za-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*\s*)\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\)\s*\{)/,lookbehind:!0,inside:c.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),c.languages.insertBefore("javascript","string",{"template-string":{pattern:/`(?:\\[\s\S]|\${(?:[^{}]|{(?:[^{}]|{[^}]*})*})+}|(?!\${)[^\\`])*`/,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\${(?:[^{}]|{(?:[^{}]|{[^}]*})*})+}/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\${|}$/,alias:"punctuation"},rest:c.languages.javascript}},string:/[\s\S]+/}}}),c.languages.markup&&c.languages.markup.tag.addInlined("script","javascript"),c.languages.js=c.languages.javascript,"undefined"!=typeof self&&self.Prism&&self.document&&document.querySelector&&(self.Prism.fileHighlight=function(e){e=e||document;var l={js:"javascript",py:"python",rb:"ruby",ps1:"powershell",psm1:"powershell",sh:"bash",bat:"batch",h:"c",tex:"latex"};Array.prototype.slice.call(e.querySelectorAll("pre[data-src]")).forEach(function(e){if(!e.hasAttribute("data-src-loaded")){for(var t,n=e.getAttribute("data-src"),r=e,i=/\blang(?:uage)?-([\w-]+)\b/i;r&&!i.test(r.className);)r=r.parentNode;if(r&&(t=(e.className.match(i)||[,""])[1]),!t){var a=(n.match(/\.(\w+)$/)||[,""])[1];t=l[a]||a}var s=document.createElement("code");s.className="language-"+t,e.textContent="",s.textContent="Loading…",e.appendChild(s);var o=new XMLHttpRequest;o.open("GET",n,!0),o.onreadystatechange=function(){4==o.readyState&&(o.status<400&&o.responseText?(s.textContent=o.responseText,c.highlightElement(s),e.setAttribute("data-src-loaded","")):400<=o.status?s.textContent="✖ Error "+o.status+" while fetching file: "+o.statusText:s.textContent="✖ Error: File does not exist or is empty")},o.send(null)}}),c.plugins.toolbar&&c.plugins.toolbar.registerButton("download-file",function(e){var t=e.element.parentNode;if(t&&/pre/i.test(t.nodeName)&&t.hasAttribute("data-src")&&t.hasAttribute("data-download-link")){var n=t.getAttribute("data-src"),r=document.createElement("a");return r.textContent=t.getAttribute("data-download-link-label")||"Download",r.setAttribute("download",""),r.href=n,r}})},document.addEventListener("DOMContentLoaded",function(){self.Prism.fileHighlight()}))});function q(e,r){var i=[],a={};return e.forEach(function(e){var t=e.level||1,n=t-1;r?@[\]^`{|}~]/g;function Z(e){return e.toLowerCase()}function B(e){if("string"!=typeof e)return"";var t=e.trim().replace(/[A-Z]+/g,Z).replace(/<[^>\d]+>/g,"").replace(I,"").replace(/\s/g,"-").replace(/-+/g,"-").replace(/^(\d)/,"_$1"),n=H[t];return n=l.call(H,t)?n+1:0,(H[t]=n)&&(t=t+"-"+n),t}function D(e,t){return'
'}B.clear=function(){H={}};var U=decodeURIComponent,Y=encodeURIComponent;function G(e){var n={};return(e=e.trim().replace(/^(\?|#|&)/,""))&&e.split("&").forEach(function(e){var t=e.replace(/\+/g," ").split("=");n[t[0]]=t[1]&&U(t[1])}),n}function V(e,t){void 0===t&&(t=[]);var n=[];for(var r in e)-1=m.length);n++){var r=t[n];if("string"==typeof r||r.content&&"string"==typeof r.content){var i=m[f],a=d.tokenStack[i],s="string"==typeof r?r:r.content,o=ne(g,i),l=s.indexOf(o);if(-1"}},video:function(e,t){return{html:'"}},audio:function(e,t){return{html:'"}},code:function(e,t){var n=e.match(/\.(\w+)$/);return"md"===(n=t||n&&n[1])&&(n="markdown"),{url:e,lang:n}}},se=function(i,e){var a=this;this.config=i,this.router=e,this.cacheTree={},this.toc=[],this.cacheTOC={},this.linkTarget=i.externalLinkTarget||"_blank",this.linkRel="_blank"===this.linkTarget?i.externalLinkRel||"noopener":"",this.contentBase=e.getBasePath();var s,t=this._initRenderer(),n=i.markdown||{};s=r(n)?n(j,t):(j.setOptions(d(n,{renderer:d(t,n.renderer)})),j),this._marked=s,this.compile=function(n){var r=!0,e=o(function(e){r=!1;var t="";return n?(t=c(n)?s(n):s.parser(n),t=i.noEmoji?t:function(e){return e.replace(/<(pre|template|code)[^>]*?>[\s\S]+?<\/(pre|template|code)>/g,function(e){return e.replace(/:/g,"__colon__")}).replace(/:(\w+?):/gi,window.emojify||D).replace(/__colon__/g,":")}(t),B.clear(),t):n})(n),t=a.router.parse().file;return r?a.toc=a.cacheTOC[t]:a.cacheTOC[t]=[].concat(a.toc),e}};se.prototype.compileEmbed=function(e,t){var n,r=ie(t),i=r.str,a=r.config;if(t=i,a.include){var s;if(W(e)||(e=K(this.contentBase,X(this.router.getCurrentPath()),e)),a.type&&(s=ae[a.type]))(n=s.call(this,e,t)).type=a.type;else{var o="code";/\.(md|markdown)/.test(e)?o="markdown":/\.mmd/.test(e)?o="mermaid":/\.html?/.test(e)?o="iframe":/\.(mp4|ogg)/.test(e)?o="video":/\.mp3/.test(e)&&(o="audio"),(n=ae[o].call(this,e,t)).type=o}return n.fragment=a.fragment,n}},se.prototype._matchNotCompileLink=function(e){for(var t=this.config.noCompileLinks||[],n=0;n'+r+""},t.code=e.code=function(e,t){return void 0===t&&(t=""),e=e.replace(/@DOCSIFY_QM@/g,"`"),''+M.highlight(e,M.languages[t]||M.languages.markup)+"
"},t.link=e.link=function(e,t,n){void 0===t&&(t="");var r="",i=ie(t),a=i.str,s=i.config;return t=a,W(e)||c._matchNotCompileLink(e)||s.ignore?(r+=0===e.indexOf("mailto:")?"":' target="'+o+'"',r+=0===e.indexOf("mailto:")?"":""!==l?' rel="'+l+'"':""):(e===c.config.homepage&&(e="README"),e=u.toURL(e,null,u.getCurrentPath())),s.target&&(r+=" target="+s.target),s.disabled&&(r+=" disabled",e="javascript:void(0)"),t&&(r+=' title="'+t+'"'),'"+n+""},t.paragraph=e.paragraph=function(e){return/^!>/.test(e)?F("tip",e):/^\?>/.test(e)?F("warn",e):""+e+"
"},t.image=e.image=function(e,t,n){var r=e,i="",a=ie(t),s=a.str,o=a.config;t=s,o["no-zoom"]&&(i+=" data-no-zoom"),t&&(i+=' title="'+t+'"');var l=o.size;if(l){var c=l.split("x");c[1]?i+="width="+c[0]+" height="+c[1]:i+="width="+c[0]}return W(e)||(r=K(h,X(u.getCurrentPath()),e)),'
"},t.list=e.list=function(e,t,n){var r=t?"ol":"ul";return"<"+r+" "+[//.test(e.split('class="task-list"')[0])?'class="task-list"':"",n&&1"+e+""+r+">"},t.listitem=e.listitem=function(e){return/^(]*>)/.test(e)?'":""+e+""},e.origin=t,e},se.prototype.sidebar=function(e,t){var n=this.toc,r=this.router.getCurrentPath(),i="";if(e)i=this.compile(e);else{for(var a=0;a{inner}"),this.cacheTree[r]=l}return i},se.prototype.subSidebar=function(e){if(e){var t=this.router.getCurrentPath(),n=this.cacheTree,r=this.toc;r[0]&&r[0].ignoreAllSubs&&r.splice(0),r[0]&&1===r[0].level&&r.shift();for(var i=0;i=t||e.classList.contains("hidden")?A(v,"add","sticky"):A(v,"remove","sticky")}}function ce(e,t,r,n){var i=[];null!=(t=m(t))&&(i=k(t,"a"));var a,s=decodeURI(e.toURL(e.getCurrentPath()));return i.sort(function(e,t){return t.href.length-e.href.length}).forEach(function(e){var t=e.getAttribute("href"),n=r?e.parentNode:e;0!==s.indexOf(t)||a?A(n,"remove","active"):(a=e,A(n,"add","active"))}),n&&(f.title=a?a.title||a.innerText+" - "+oe:oe),a}function ue(e,t){for(var n=0;nthis.end&&e>=this.next}[this.direction]}},{key:"_defaultEase",value:function(e,t,n,r){return(e/=r/2)<1?n/2*e*e+t:-n/2*(--e*(e-2)-1)+t}}]),fe);function fe(){var e=0o){t=t||u;break}t=u}if(t){var h=me[xe(decodeURIComponent(e),t.getAttribute("data-id"))];if(h&&h!==a&&(a&&a.classList.remove("active"),h.classList.add("active"),a=h,!ve&&v.classList.contains("sticky"))){var p=n.clientHeight,d=a.offsetTop+a.clientHeight+40,g=d-0=i.scrollTop&&d<=i.scrollTop+p?i.scrollTop:g?0:d-p;n.scrollTop=f}}}}function xe(e,t){return e+"?id="+t}function _e(e,t){if(t){var n=y("#"+t);n&&function(e){be&&be.stop(),ye=!1,be=new ge({start:window.pageYOffset,end:e.getBoundingClientRect().top+window.pageYOffset,duration:500}).on("tick",function(e){return window.scrollTo(0,e)}).on("done",function(){ye=!0,be=null}).begin()}(n);var r=me[xe(e,t)],i=y(m(".sidebar"),"li.active");i&&i.classList.remove("active"),r&&r.classList.add("active")}}var Se=f.scrollingElement||f.documentElement;var Ae={};function $e(e,i){var s=e.compiler,a=e.raw;void 0===a&&(a="");var t=e.fetch,n=Ae[a];if(n){var r=n.slice();return r.links=n.links,i(r)}var o=s._marked,l=o.lexer(a),c=[],u=o.InlineLexer.rules.link,h=l.links;l.forEach(function(e,a){"paragraph"===e.type&&(e.text=e.text.replace(new RegExp(u.source,"g"),function(e,t,n,r){var i=s.compileEmbed(n,r);return i&&c.push({index:a,embed:i}),e}))});var p=0;!function(e,a){var t,n=e.embedTokens,s=e.compile,o=(e.fetch,0),l=1;if(!n.length)return a({});for(;t=n[o++];){var r=function(i){return function(e){var t;if(e)if("markdown"===i.embed.type)t=s.lexer(e);else if("code"===i.embed.type){if(i.embed.fragment){var n=i.embed.fragment,r=new RegExp("(?:###|\\/\\/\\/)\\s*\\["+n+"\\]([\\s\\S]*)(?:###|\\/\\/\\/)\\s*\\["+n+"\\]");e=((e.match(r)||[])[1]||"").trim()}t=s.lexer("```"+i.embed.lang+"\n"+e.replace(/`/g,"@DOCSIFY_QM@")+"\n```\n")}else"mermaid"===i.embed.type?(t=[{type:"html",text:'
\n'+e+"\n
"}]).links={}:(t=[{type:"html",text:e}]).links={};a({token:i,embedToken:t}),++l>=o&&a({})}}(t);t.embed.url?R(t.embed.url).then(r):r(t.embed.html)}}({compile:o,embedTokens:c,fetch:t},function(e){var t=e.embedToken,n=e.token;if(n){var r=n.index+p;d(h,t.links),l=l.slice(0,r).concat(t,l.slice(r+1)),p+=t.length-1}else Ae[a]=l.concat(),l.links=Ae[a].links=h,i(l)})}function Ce(){var e=k(".markdown-section>script").filter(function(e){return!/template/.test(e.type)})[0];if(!e)return!1;var t=e.innerText.trim();if(!t)return!1;setTimeout(function(e){window.__EXECUTE_RESULT__=new Function(t)()},0)}function Ee(e,t,n){return t="function"==typeof n?n(t):"string"==typeof n?function(r,i){var a=[],s=0;return r.replace(P,function(t,e,n){a.push(r.substring(s,n-1)),s=n+=t.length+1,a.push(i&&i[t]||function(e){return("00"+("string"==typeof z[t]?e[z[t]]():z[t](e))).slice(-t.length)})}),s!==r.length&&a.push(r.substring(s)),function(e){for(var t="",n=0,r=e||new Date;n404 - Not found",this._renderTo(".markdown-section",e),this.config.loadSidebar||this._renderSidebar(),!1===this.config.executeScript||void 0===window.Vue||Ce()?this.config.executeScript&&Ce():setTimeout(function(e){var t=window.__EXECUTE_RESULT__;t&&t.$destroy&&t.$destroy(),window.__EXECUTE_RESULT__=(new window.Vue).$mount("#main")},0)}function Le(e){var t=e.config;e.compiler=new se(t,e.router),window.__current_docsify_compiler__=e.compiler;var n=t.el||"#app",r=y("nav")||w("nav"),i=y(n),a="",s=v;if(i){if(t.repo&&(a+=function(e,t){return e?(/\/\//.test(e)||(e="https://github.com/"+e),''):""}(t.repo,t.cornerExternalLinkTarge)),t.coverpage&&(a+=function(){var e=", 100%, 85%";return''}()),t.logo){var o=/^data:image/.test(t.logo),l=/(?:http[s]?:)?\/\//.test(t.logo),c=/^\./.test(t.logo);o||l||c||(t.logo=K(e.router.getBasePath(),t.logo))}a+=function(e){var t=e.name?u(e.name):"",n='';return(g?n+"":""+n)+''}(t),e._renderTo(i,a,!0)}else e.rendered=!0;t.mergeNavbar&&g?s=y(".sidebar"):(r.classList.add("app-nav"),t.repo||r.classList.add("no-badge")),t.loadNavbar&&x(s,r),t.themeColor&&(f.head.appendChild(w("div",function(e){return""}(t.themeColor)).firstElementChild),function(n){if(!(window.CSS&&window.CSS.supports&&window.CSS.supports("(--v:red)"))){var e=k("style:not(.inserted),link");[].forEach.call(e,function(e){if("STYLE"===e.nodeName)O(e,n);else if("LINK"===e.nodeName){var t=e.getAttribute("href");if(!/\.css$/.test(t))return;R(t).then(function(e){var t=w("style",e);b.appendChild(t),O(t,n)})}})}}(t.themeColor)),e._updateRender(),A(v,"ready")}var Te={};function Re(e){this.config=e}function Oe(e){var t=location.href.indexOf("#");location.replace(location.href.slice(0,0<=t?t:0)+"#"+e)}Re.prototype.getBasePath=function(){return this.config.basePath},Re.prototype.getFile=function(e,t){void 0===e&&(e=this.getCurrentPath());var n=this.config,r=this.getBasePath(),i="string"==typeof n.ext?n.ext:".md";return e=(e=function(e,t){return new RegExp("\\.("+t.replace(/^\./,"")+"|html)$","g").test(e)?e:/\/$/g.test(e)?e+"README"+t:""+e+t}(e=n.alias?function e(t,n,r){var i=Object.keys(n).filter(function(e){return(Te[e]||(Te[e]=new RegExp("^"+e+"$"))).test(t)&&t!==r})[0];return i?e(t.replace(Te[i],n[i]),n,t):t}(e,n.alias):e,i))==="/README"+i&&n.homepage||e,e=W(e)?e:K(r,e),t&&(e=e.replace(new RegExp("^"+r),"")),e},Re.prototype.onchange=function(e){void 0===e&&(e=h),e()},Re.prototype.getCurrentPath=function(){},Re.prototype.normalize=function(){},Re.prototype.parse=function(){},Re.prototype.toURL=function(e,t,n){var r=n&&"#"===e[0],i=this.parse(te(e));if(i.query=d({},i.query,t),e=(e=i.path+V(i.query)).replace(/\.md(\?)|\.md$/,"$1"),r){var a=n.indexOf("?");e=(0([^<]*?)$');if(i){if("color"===i[2])n.style.background=i[1]+(i[3]||"");else{var a=i[1];A(n,"add","has-mask"),W(i[1])||(a=K(this.router.getBasePath(),i[1])),n.style.backgroundImage="url("+a+")",n.style.backgroundSize="cover",n.style.backgroundPosition="center center"}r=r.replace(i[0],"")}this._renderTo(".cover-main",r),le()}else A(n,"remove","show")},Ze._updateRender=function(){!function(e){var t=m(".app-name-link"),n=e.config.nameLink,r=e.route.path;if(t)if(c(e.config.nameLink))t.setAttribute("href",n);else if("object"==typeof n){var i=Object.keys(n).filter(function(e){return-1': '>',
+ '"': '"',
+ '\'': ''',
+ '/': '/'
+ };
+
+ return String(string).replace(/[&<>"'/]/g, function (s) { return entityMap[s]; })
+ }
+
+ function getAllPaths(router) {
+ var paths = [];
+
+ Docsify.dom.findAll('.sidebar-nav a:not(.section-link):not([data-nosearch])').forEach(function (node) {
+ var href = node.href;
+ var originHref = node.getAttribute('href');
+ var path = router.parse(href).path;
+
+ if (
+ path &&
+ paths.indexOf(path) === -1 &&
+ !Docsify.util.isAbsolutePath(originHref)
+ ) {
+ paths.push(path);
+ }
+ });
+
+ return paths
+ }
+
+ function saveData(maxAge, expireKey, indexKey) {
+ localStorage.setItem(expireKey, Date.now() + maxAge);
+ localStorage.setItem(indexKey, JSON.stringify(INDEXS));
+ }
+
+ function genIndex(path, content, router, depth) {
+ if ( content === void 0 ) content = '';
+
+ var tokens = window.marked.lexer(content);
+ var slugify = window.Docsify.slugify;
+ var index = {};
+ var slug;
+
+ tokens.forEach(function (token) {
+ if (token.type === 'heading' && token.depth <= depth) {
+ slug = router.toURL(path, {id: slugify(token.text)});
+ index[slug] = {slug: slug, title: token.text, body: ''};
+ } else {
+ if (!slug) {
+ return
+ }
+ if (!index[slug]) {
+ index[slug] = {slug: slug, title: '', body: ''};
+ } else if (index[slug].body) {
+ index[slug].body += '\n' + (token.text || '');
+ } else {
+ index[slug].body = token.text;
+ }
+ }
+ });
+ slugify.clear();
+ return index
+ }
+
+ /**
+ * @param {String} query
+ * @returns {Array}
+ */
+ function search(query) {
+ var matchingResults = [];
+ var data = [];
+ Object.keys(INDEXS).forEach(function (key) {
+ data = data.concat(Object.keys(INDEXS[key]).map(function (page) { return INDEXS[key][page]; }));
+ });
+
+ query = query.trim();
+ var keywords = query.split(/[\s\-,\\/]+/);
+ if (keywords.length !== 1) {
+ keywords = [].concat(query, keywords);
+ }
+
+ var loop = function ( i ) {
+ var post = data[i];
+ var matchesScore = 0;
+ var resultStr = '';
+ var postTitle = post.title && post.title.trim();
+ var postContent = post.body && post.body.trim();
+ var postUrl = post.slug || '';
+
+ if (postTitle) {
+ keywords.forEach( function (keyword) {
+ // From https://github.com/sindresorhus/escape-string-regexp
+ var regEx = new RegExp(
+ keyword.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&'),
+ 'gi'
+ );
+ var indexTitle = -1;
+ var indexContent = -1;
+
+ indexTitle = postTitle ? postTitle.search(regEx) : -1;
+ indexContent = postContent ? postContent.search(regEx) : -1;
+
+ if (indexTitle >= 0 || indexContent >= 0) {
+ matchesScore += indexTitle >= 0 ? 3 : indexContent >= 0 ? 2 : 0;
+ if (indexContent < 0) {
+ indexContent = 0;
+ }
+
+ var start = 0;
+ var end = 0;
+
+ start = indexContent < 11 ? 0 : indexContent - 10;
+ end = start === 0 ? 70 : indexContent + keyword.length + 60;
+
+ if (end > postContent.length) {
+ end = postContent.length;
+ }
+
+ var matchContent =
+ '...' +
+ escapeHtml(postContent)
+ .substring(start, end)
+ .replace(regEx, ("" + keyword + "")) +
+ '...';
+
+ resultStr += matchContent;
+ }
+ });
+
+ if (matchesScore > 0) {
+ var matchingPost = {
+ title: escapeHtml(postTitle),
+ content: postContent ? resultStr : '',
+ url: postUrl,
+ score: matchesScore
+ };
+
+ matchingResults.push(matchingPost);
+ }
+ }
+ };
+
+ for (var i = 0; i < data.length; i++) loop( i );
+
+ return matchingResults.sort(function (r1, r2) { return r2.score - r1.score; });
+ }
+
+ function init(config, vm) {
+ var isAuto = config.paths === 'auto';
+
+ var expireKey = resolveExpireKey(config.namespace);
+ var indexKey = resolveIndexKey(config.namespace);
+
+ var isExpired = localStorage.getItem(expireKey) < Date.now();
+
+ INDEXS = JSON.parse(localStorage.getItem(indexKey));
+
+ if (isExpired) {
+ INDEXS = {};
+ } else if (!isAuto) {
+ return
+ }
+
+ var paths = isAuto ? getAllPaths(vm.router) : config.paths;
+ var len = paths.length;
+ var count = 0;
+
+ paths.forEach(function (path) {
+ if (INDEXS[path]) {
+ return count++
+ }
+
+ Docsify
+ .get(vm.router.getFile(path), false, vm.config.requestHeaders)
+ .then(function (result) {
+ INDEXS[path] = genIndex(path, result, vm.router, config.depth);
+ len === ++count && saveData(config.maxAge, expireKey, indexKey);
+ });
+ });
+ }
+
+ var NO_DATA_TEXT = '';
+ var options;
+
+ function style() {
+ var code = "\n.sidebar {\n padding-top: 0;\n}\n\n.search {\n margin-bottom: 20px;\n padding: 6px;\n border-bottom: 1px solid #eee;\n}\n\n.search .input-wrap {\n display: flex;\n align-items: center;\n}\n\n.search .results-panel {\n display: none;\n}\n\n.search .results-panel.show {\n display: block;\n}\n\n.search input {\n outline: none;\n border: none;\n width: 100%;\n padding: 0 7px;\n line-height: 36px;\n font-size: 14px;\n border: 1px solid transparent;\n}\n\n.search input:focus {\n box-shadow: 0 0 5px var(--theme-color, #42b983);\n border: 1px solid var(--theme-color, #42b983);\n}\n\n.search input::-webkit-search-decoration,\n.search input::-webkit-search-cancel-button,\n.search input {\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n}\n.search .clear-button {\n width: 36px;\n text-align: right;\n display: none;\n}\n\n.search .clear-button.show {\n display: block;\n}\n\n.search .clear-button svg {\n transform: scale(.5);\n}\n\n.search h2 {\n font-size: 17px;\n margin: 10px 0;\n}\n\n.search a {\n text-decoration: none;\n color: inherit;\n}\n\n.search .matching-post {\n border-bottom: 1px solid #eee;\n}\n\n.search .matching-post:last-child {\n border-bottom: 0;\n}\n\n.search p {\n font-size: 14px;\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n}\n\n.search p.empty {\n text-align: center;\n}\n\n.app-name.hide, .sidebar-nav.hide {\n display: none;\n}";
+
+ Docsify.dom.style(code);
+ }
+
+ function tpl(defaultValue) {
+ if ( defaultValue === void 0 ) defaultValue = '';
+
+ var html =
+ "\n \n ";
+ var el = Docsify.dom.create('div', html);
+ var aside = Docsify.dom.find('aside');
+
+ Docsify.dom.toggleClass(el, 'search');
+ Docsify.dom.before(aside, el);
+ }
+
+ function doSearch(value) {
+ var $search = Docsify.dom.find('div.search');
+ var $panel = Docsify.dom.find($search, '.results-panel');
+ var $clearBtn = Docsify.dom.find($search, '.clear-button');
+ var $sidebarNav = Docsify.dom.find('.sidebar-nav');
+ var $appName = Docsify.dom.find('.app-name');
+
+ if (!value) {
+ $panel.classList.remove('show');
+ $clearBtn.classList.remove('show');
+ $panel.innerHTML = '';
+
+ if (options.hideOtherSidebarContent) {
+ $sidebarNav.classList.remove('hide');
+ $appName.classList.remove('hide');
+ }
+ return
+ }
+ var matchs = search(value);
+
+ var html = '';
+ matchs.forEach(function (post) {
+ html += "";
+ });
+
+ $panel.classList.add('show');
+ $clearBtn.classList.add('show');
+ $panel.innerHTML = html || ("" + NO_DATA_TEXT + "
");
+ if (options.hideOtherSidebarContent) {
+ $sidebarNav.classList.add('hide');
+ $appName.classList.add('hide');
+ }
+ }
+
+ function bindEvents() {
+ var $search = Docsify.dom.find('div.search');
+ var $input = Docsify.dom.find($search, 'input');
+ var $inputWrap = Docsify.dom.find($search, '.input-wrap');
+
+ var timeId;
+ // Prevent to Fold sidebar
+ Docsify.dom.on(
+ $search,
+ 'click',
+ function (e) { return e.target.tagName !== 'A' && e.stopPropagation(); }
+ );
+ Docsify.dom.on($input, 'input', function (e) {
+ clearTimeout(timeId);
+ timeId = setTimeout(function (_) { return doSearch(e.target.value.trim()); }, 100);
+ });
+ Docsify.dom.on($inputWrap, 'click', function (e) {
+ // Click input outside
+ if (e.target.tagName !== 'INPUT') {
+ $input.value = '';
+ doSearch();
+ }
+ });
+ }
+
+ function updatePlaceholder(text, path) {
+ var $input = Docsify.dom.getNode('.search input[type="search"]');
+
+ if (!$input) {
+ return
+ }
+ if (typeof text === 'string') {
+ $input.placeholder = text;
+ } else {
+ var match = Object.keys(text).filter(function (key) { return path.indexOf(key) > -1; })[0];
+ $input.placeholder = text[match];
+ }
+ }
+
+ function updateNoData(text, path) {
+ if (typeof text === 'string') {
+ NO_DATA_TEXT = text;
+ } else {
+ var match = Object.keys(text).filter(function (key) { return path.indexOf(key) > -1; })[0];
+ NO_DATA_TEXT = text[match];
+ }
+ }
+
+ function updateOptions(opts) {
+ options = opts;
+ }
+
+ function init$1(opts, vm) {
+ var keywords = vm.router.parse().query.s;
+
+ updateOptions(opts);
+ style();
+ tpl(keywords);
+ bindEvents();
+ keywords && setTimeout(function (_) { return doSearch(keywords); }, 500);
+ }
+
+ function update(opts, vm) {
+ updateOptions(opts);
+ updatePlaceholder(opts.placeholder, vm.route.path);
+ updateNoData(opts.noData, vm.route.path);
+ }
+
+ var CONFIG = {
+ placeholder: 'Type to search',
+ noData: 'No Results!',
+ paths: 'auto',
+ depth: 2,
+ maxAge: 86400000, // 1 day
+ hideOtherSidebarContent: false,
+ namespace: undefined
+ };
+
+ var install = function (hook, vm) {
+ var util = Docsify.util;
+ var opts = vm.config.search || CONFIG;
+
+ if (Array.isArray(opts)) {
+ CONFIG.paths = opts;
+ } else if (typeof opts === 'object') {
+ CONFIG.paths = Array.isArray(opts.paths) ? opts.paths : 'auto';
+ CONFIG.maxAge = util.isPrimitive(opts.maxAge) ? opts.maxAge : CONFIG.maxAge;
+ CONFIG.placeholder = opts.placeholder || CONFIG.placeholder;
+ CONFIG.noData = opts.noData || CONFIG.noData;
+ CONFIG.depth = opts.depth || CONFIG.depth;
+ CONFIG.hideOtherSidebarContent = opts.hideOtherSidebarContent || CONFIG.hideOtherSidebarContent;
+ CONFIG.namespace = opts.namespace || CONFIG.namespace;
+ }
+
+ var isAuto = CONFIG.paths === 'auto';
+
+ hook.mounted(function (_) {
+ init$1(CONFIG, vm);
+ !isAuto && init(CONFIG, vm);
+ });
+ hook.doneEach(function (_) {
+ update(CONFIG, vm);
+ isAuto && init(CONFIG, vm);
+ });
+ };
+
+ $docsify.plugins = [].concat(install, $docsify.plugins);
+
+}());
\ No newline at end of file
diff --git a/static/sychronize.png b/static/sychronize.png
index 978fcc6..2e65e74 100755
Binary files a/static/sychronize.png and b/static/sychronize.png differ
diff --git a/static/up-70a9e95e6c1208288334341bdb54bd59c17.png b/static/up-70a9e95e6c1208288334341bdb54bd59c17.png
new file mode 100644
index 0000000..9d65b32
Binary files /dev/null and b/static/up-70a9e95e6c1208288334341bdb54bd59c17.png differ
diff --git a/static/v2-310cc7857eabd42e324c109b5ca85b1d_b.png b/static/v2-310cc7857eabd42e324c109b5ca85b1d_b.png
new file mode 100644
index 0000000..a8ba3ae
Binary files /dev/null and b/static/v2-310cc7857eabd42e324c109b5ca85b1d_b.png differ
diff --git a/static/v2-880b5e0866906160b663827f961d8360_b.jpg b/static/v2-880b5e0866906160b663827f961d8360_b.jpg
new file mode 100644
index 0000000..399766e
Binary files /dev/null and b/static/v2-880b5e0866906160b663827f961d8360_b.jpg differ
diff --git a/static/v2-90179fa933c0a389ffa6ac04e244a58f_b.jpg b/static/v2-90179fa933c0a389ffa6ac04e244a58f_b.jpg
new file mode 100644
index 0000000..d5e6167
Binary files /dev/null and b/static/v2-90179fa933c0a389ffa6ac04e244a58f_b.jpg differ
diff --git a/static/vue.css b/static/vue.css
index 6694c27..46e203b 100644
--- a/static/vue.css
+++ b/static/vue.css
@@ -1 +1 @@
-@import url("https://fonts.googleapis.com/css?family=Roboto+Mono|Source+Sans+Pro:300,400,600");*{-webkit-font-smoothing:antialiased;-webkit-overflow-scrolling:touch;-webkit-tap-highlight-color:rgba(0,0,0,0);-webkit-text-size-adjust:none;-webkit-touch-callout:none;box-sizing:border-box}body:not(.ready){overflow:hidden}body:not(.ready) .app-nav,body:not(.ready)>nav,body:not(.ready) [data-cloak]{display:none}div#app{font-size:30px;font-weight:lighter;margin:40vh auto;text-align:center}div#app:empty:before{content:"Loading..."}.emoji{height:1.2rem;vertical-align:middle}.progress{background-color:var(--theme-color,#42b983);height:2px;left:0;position:fixed;right:0;top:0;transition:width .2s,opacity .4s;width:0;z-index:999999}.search .search-keyword,.search a:hover{color:var(--theme-color,#42b983)}.search .search-keyword{font-style:normal;font-weight:700}body,html{height:100%}body{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;color:#34495e;font-family:Source Sans Pro,Helvetica Neue,Arial,sans-serif;font-size:15px;letter-spacing:0;margin:0;overflow-x:hidden}img{max-width:100%}a[disabled]{cursor:not-allowed;opacity:.6}kbd{border:1px solid #ccc;border-radius:3px;display:inline-block;font-size:12px!important;line-height:12px;margin-bottom:3px;padding:3px 5px;vertical-align:middle}li input[type=checkbox]{margin:0 .2em .25em 0;vertical-align:middle}.app-nav{margin:25px 60px 0 0;position:absolute;right:0;text-align:right;z-index:10}.app-nav.no-badge{margin-right:25px}.app-nav p{margin:0}.app-nav>a{margin:0 1rem;padding:5px 0}.app-nav li,.app-nav ul{display:inline-block;list-style:none;margin:0}.app-nav a{color:inherit;font-size:16px;text-decoration:none;transition:color .3s}.app-nav a.active,.app-nav a:hover{color:var(--theme-color,#42b983)}.app-nav a.active{border-bottom:2px solid var(--theme-color,#42b983)}.app-nav li{display:inline-block;margin:0 1rem;padding:5px 0;position:relative}.app-nav li ul{background-color:#fff;border:1px solid;border-color:#ddd #ddd #ccc;border-radius:4px;box-sizing:border-box;display:none;max-height:calc(100vh - 61px);overflow-y:auto;padding:10px 0;position:absolute;right:-15px;text-align:left;top:100%;white-space:nowrap}.app-nav li ul li{display:block;font-size:14px;line-height:1rem;margin:8px 14px;white-space:nowrap}.app-nav li ul a{display:block;font-size:inherit;margin:0;padding:0}.app-nav li ul a.active{border-bottom:0}.app-nav li:hover ul{display:block}.github-corner{border-bottom:0;position:fixed;right:0;text-decoration:none;top:0;z-index:1}.github-corner:hover .octo-arm{-webkit-animation:octocat-wave .56s ease-in-out;animation:octocat-wave .56s ease-in-out}.github-corner svg{color:#fff;fill:var(--theme-color,#42b983);height:80px;width:80px}main{display:block;position:relative;width:100vw;height:100%;z-index:0}main.hidden{display:none}.anchor{display:inline-block;text-decoration:none;transition:all .3s}.anchor span{color:#34495e}.anchor:hover{text-decoration:underline}.sidebar{border-right:1px solid rgba(0,0,0,.07);overflow-y:auto;padding:40px 0 0;position:absolute;top:0;bottom:0;left:0;transition:transform .25s ease-out;width:300px;z-index:20}.sidebar>h1{margin:0 auto 1rem;font-size:1.5rem;font-weight:300;text-align:center}.sidebar>h1 a{color:inherit;text-decoration:none}.sidebar>h1 .app-nav{display:block;position:static}.sidebar .sidebar-nav{line-height:2em;padding-bottom:40px}.sidebar li.collapse .app-sub-sidebar{display:none}.sidebar ul{margin:0 0 0 15px;padding:0}.sidebar li>p{font-weight:700;margin:0}.sidebar ul,.sidebar ul li{list-style:none}.sidebar ul li a{border-bottom:none;display:block}.sidebar ul li ul{padding-left:20px}.sidebar::-webkit-scrollbar{width:4px}.sidebar::-webkit-scrollbar-thumb{background:transparent;border-radius:4px}.sidebar:hover::-webkit-scrollbar-thumb{background:hsla(0,0%,53.3%,.4)}.sidebar:hover::-webkit-scrollbar-track{background:hsla(0,0%,53.3%,.1)}.sidebar-toggle{background-color:transparent;background-color:hsla(0,0%,100%,.8);border:0;outline:none;padding:10px;position:absolute;bottom:0;left:0;text-align:center;transition:opacity .3s;width:284px;z-index:30}.sidebar-toggle .sidebar-toggle-button:hover{opacity:.4}.sidebar-toggle span{background-color:var(--theme-color,#42b983);display:block;margin-bottom:4px;width:16px;height:2px}body.sticky .sidebar,body.sticky .sidebar-toggle{position:fixed}.content{padding-top:60px;position:absolute;top:0;right:0;bottom:0;left:300px;transition:left .25s ease}.markdown-section{margin:0 auto;max-width:800px;padding:30px 15px 40px;position:relative}.markdown-section>*{box-sizing:border-box;font-size:inherit}.markdown-section>:first-child{margin-top:0!important}.markdown-section hr{border:none;border-bottom:1px solid #eee;margin:2em 0}.markdown-section iframe{border:1px solid #eee;width:1px;min-width:100%}.markdown-section table{border-collapse:collapse;border-spacing:0;display:block;margin-bottom:1rem;overflow:auto;width:100%}.markdown-section th{font-weight:700}.markdown-section td,.markdown-section th{border:1px solid #ddd;padding:6px 13px}.markdown-section tr{border-top:1px solid #ccc}.markdown-section p.tip,.markdown-section tr:nth-child(2n){background-color:#f8f8f8}.markdown-section p.tip{border-bottom-right-radius:2px;border-left:4px solid #f66;border-top-right-radius:2px;margin:2em 0;padding:12px 24px 12px 30px;position:relative}.markdown-section p.tip:before{background-color:#f66;border-radius:100%;color:#fff;content:"!";font-family:Dosis,Source Sans Pro,Helvetica Neue,Arial,sans-serif;font-size:14px;font-weight:700;left:-12px;line-height:20px;position:absolute;height:20px;width:20px;text-align:center;top:14px}.markdown-section p.tip code{background-color:#efefef}.markdown-section p.tip em{color:#34495e}.markdown-section p.warn{background:rgba(66,185,131,.1);border-radius:2px;padding:1rem}.markdown-section ul.task-list>li{list-style-type:none}body.close .sidebar{transform:translateX(-300px)}body.close .sidebar-toggle{width:auto}body.close .content{left:0}@media print{.app-nav,.github-corner,.sidebar,.sidebar-toggle{display:none}}@media screen and (max-width:768px){.github-corner,.sidebar,.sidebar-toggle{position:fixed}.app-nav{margin-top:16px}.app-nav li ul{top:30px}main{height:auto;overflow-x:hidden}.sidebar{left:-300px;transition:transform .25s ease-out}.content{left:0;max-width:100vw;position:static;padding-top:20px;transition:transform .25s ease}.app-nav,.github-corner{transition:transform .25s ease-out}.sidebar-toggle{background-color:transparent;width:auto;padding:30px 30px 10px 10px}body.close .sidebar{transform:translateX(300px)}body.close .sidebar-toggle{background-color:hsla(0,0%,100%,.8);transition:background-color 1s;width:284px;padding:10px}body.close .content{transform:translateX(300px)}body.close .app-nav,body.close .github-corner{display:none}.github-corner:hover .octo-arm{-webkit-animation:none;animation:none}.github-corner .octo-arm{-webkit-animation:octocat-wave .56s ease-in-out;animation:octocat-wave .56s ease-in-out}}@-webkit-keyframes octocat-wave{0%,to{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@keyframes octocat-wave{0%,to{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}section.cover{align-items:center;background-position:50%;background-repeat:no-repeat;background-size:cover;height:100vh;display:none}section.cover.show{display:flex}section.cover.has-mask .mask{background-color:#fff;opacity:.8;position:absolute;top:0;height:100%;width:100%}section.cover .cover-main{flex:1;margin:-20px 16px 0;text-align:center;z-index:1}section.cover a{color:inherit}section.cover a,section.cover a:hover{text-decoration:none}section.cover p{line-height:1.5rem;margin:1em 0}section.cover h1{color:inherit;font-size:2.5rem;font-weight:300;margin:.625rem 0 2.5rem;position:relative;text-align:center}section.cover h1 a{display:block}section.cover h1 small{bottom:-.4375rem;font-size:1rem;position:absolute}section.cover blockquote{font-size:1.5rem;text-align:center}section.cover ul{line-height:1.8;list-style-type:none;margin:1em auto;max-width:500px;padding:0}section.cover .cover-main>p:last-child a{border-radius:2rem;border:1px solid var(--theme-color,#42b983);box-sizing:border-box;color:var(--theme-color,#42b983);display:inline-block;font-size:1.05rem;letter-spacing:.1rem;margin:.5rem 1rem;padding:.75em 2rem;text-decoration:none;transition:all .15s ease}section.cover .cover-main>p:last-child a:last-child{background-color:var(--theme-color,#42b983);color:#fff}section.cover .cover-main>p:last-child a:last-child:hover{color:inherit;opacity:.8}section.cover .cover-main>p:last-child a:hover{color:inherit}section.cover blockquote>p>a{border-bottom:2px solid var(--theme-color,#42b983);transition:color .3s}section.cover blockquote>p>a:hover{color:var(--theme-color,#42b983)}.sidebar,body{background-color:#fff}.sidebar{color:#364149}.sidebar li{margin:6px 0}.sidebar ul li a{color:#505d6b;font-size:14px;font-weight:400;overflow:hidden;text-decoration:none;text-overflow:ellipsis;white-space:nowrap}.sidebar ul li a:hover{text-decoration:underline}.sidebar ul li ul{padding:0}.sidebar ul li.active>a{border-right:2px solid;color:var(--theme-color,#42b983);font-weight:600}.app-sub-sidebar li:before{content:"-";padding-right:4px;float:left}.markdown-section h1,.markdown-section h2,.markdown-section h3,.markdown-section h4,.markdown-section strong{color:#2c3e50;font-weight:600}.markdown-section a{color:var(--theme-color,#42b983);font-weight:600}.markdown-section h1{font-size:2rem;margin:0 0 1rem}.markdown-section h2{font-size:1.75rem;margin:45px 0 .8rem}.markdown-section h3{font-size:1.5rem;margin:40px 0 .6rem}.markdown-section h4{font-size:1.25rem}.markdown-section h5{font-size:1rem}.markdown-section h6{color:#777;font-size:1rem}.markdown-section figure,.markdown-section p{margin:1.2em 0}.markdown-section ol,.markdown-section p,.markdown-section ul{line-height:1.6rem;word-spacing:.05rem}.markdown-section ol,.markdown-section ul{padding-left:1.5rem}.markdown-section blockquote{border-left:4px solid var(--theme-color,#42b983);color:#858585;margin:2em 0;padding-left:20px}.markdown-section blockquote p{font-weight:600;margin-left:0}.markdown-section iframe{margin:1em 0}.markdown-section em{color:#7f8c8d}.markdown-section code{border-radius:2px;color:#e96900;font-size:.8rem;margin:0 2px;padding:3px 5px;white-space:pre-wrap}.markdown-section code,.markdown-section pre{background-color:#f8f8f8;font-family:Roboto Mono,Monaco,courier,monospace}.markdown-section pre{-moz-osx-font-smoothing:initial;-webkit-font-smoothing:initial;line-height:1.5rem;margin:1.2em 0;overflow:auto;padding:0 1.4rem;position:relative;word-wrap:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#8e908c}.token.namespace{opacity:.7}.token.boolean,.token.number{color:#c76b29}.token.punctuation{color:#525252}.token.property{color:#c08b30}.token.tag{color:#2973b7}.token.string{color:var(--theme-color,#42b983)}.token.selector{color:#6679cc}.token.attr-name{color:#2973b7}.language-css .token.string,.style .token.string,.token.entity,.token.url{color:#22a2c9}.token.attr-value,.token.control,.token.directive,.token.unit{color:var(--theme-color,#42b983)}.token.function,.token.keyword{color:#e96900}.token.atrule,.token.regex,.token.statement{color:#22a2c9}.token.placeholder,.token.variable{color:#3d8fd1}.token.deleted{text-decoration:line-through}.token.inserted{border-bottom:1px dotted #202746;text-decoration:none}.token.italic{font-style:italic}.token.bold,.token.important{font-weight:700}.token.important{color:#c94922}.token.entity{cursor:help}.markdown-section pre>code{-moz-osx-font-smoothing:initial;-webkit-font-smoothing:initial;background-color:#f8f8f8;border-radius:2px;color:#525252;display:block;font-family:Roboto Mono,Monaco,courier,monospace;font-size:.8rem;line-height:inherit;margin:0 2px;max-width:inherit;overflow:inherit;padding:2.2em 5px;white-space:inherit}.markdown-section code:after,.markdown-section code:before{letter-spacing:.05rem}code .token{-moz-osx-font-smoothing:initial;-webkit-font-smoothing:initial;min-height:1.5rem;position:relative;left:auto}pre:after{color:#ccc;content:attr(data-lang);font-size:.6rem;font-weight:600;height:15px;line-height:15px;padding:5px 10px 0;position:absolute;right:0;text-align:right;top:0}
\ No newline at end of file
+@import url("https://fonts.loli.net/css?family=Roboto+Mono|Source+Sans+Pro:300,400,600");*{-webkit-font-smoothing:antialiased;-webkit-overflow-scrolling:touch;-webkit-tap-highlight-color:rgba(0,0,0,0);-webkit-text-size-adjust:none;-webkit-touch-callout:none;box-sizing:border-box}body:not(.ready){overflow:hidden}body:not(.ready) .app-nav,body:not(.ready)>nav,body:not(.ready) [data-cloak]{display:none}div#app{font-size:30px;font-weight:lighter;margin:40vh auto;text-align:center}div#app:empty:before{content:"Loading..."}.emoji{height:1.2rem;vertical-align:middle}.progress{background-color:var(--theme-color,#42b983);height:2px;left:0;position:fixed;right:0;top:0;transition:width .2s,opacity .4s;width:0;z-index:999999}.search .search-keyword,.search a:hover{color:var(--theme-color,#42b983)}.search .search-keyword{font-style:normal;font-weight:700}body,html{height:100%}body{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;color:#34495e;font-family:Source Sans Pro,Helvetica Neue,Arial,sans-serif;font-size:15px;letter-spacing:0;margin:0;overflow-x:hidden}img{max-width:100%}a[disabled]{cursor:not-allowed;opacity:.6}kbd{border:1px solid #ccc;border-radius:3px;display:inline-block;font-size:12px!important;line-height:12px;margin-bottom:3px;padding:3px 5px;vertical-align:middle}li input[type=checkbox]{margin:0 .2em .25em 0;vertical-align:middle}.app-nav{margin:25px 60px 0 0;position:absolute;right:0;text-align:right;z-index:10}.app-nav.no-badge{margin-right:25px}.app-nav p{margin:0}.app-nav>a{margin:0 1rem;padding:5px 0}.app-nav li,.app-nav ul{display:inline-block;list-style:none;margin:0}.app-nav a{color:inherit;font-size:16px;text-decoration:none;transition:color .3s}.app-nav a.active,.app-nav a:hover{color:var(--theme-color,#42b983)}.app-nav a.active{border-bottom:2px solid var(--theme-color,#42b983)}.app-nav li{display:inline-block;margin:0 1rem;padding:5px 0;position:relative}.app-nav li ul{background-color:#fff;border:1px solid;border-color:#ddd #ddd #ccc;border-radius:4px;box-sizing:border-box;display:none;max-height:calc(100vh - 61px);overflow-y:auto;padding:10px 0;position:absolute;right:-15px;text-align:left;top:100%;white-space:nowrap}.app-nav li ul li{display:block;font-size:14px;line-height:1rem;margin:8px 14px;white-space:nowrap}.app-nav li ul a{display:block;font-size:inherit;margin:0;padding:0}.app-nav li ul a.active{border-bottom:0}.app-nav li:hover ul{display:block}.github-corner{border-bottom:0;position:fixed;right:0;text-decoration:none;top:0;z-index:1}.github-corner:hover .octo-arm{-webkit-animation:octocat-wave .56s ease-in-out;animation:octocat-wave .56s ease-in-out}.github-corner svg{color:#fff;fill:var(--theme-color,#42b983);height:80px;width:80px}main{display:block;position:relative;width:100vw;height:100%;z-index:0}main.hidden{display:none}.anchor{display:inline-block;text-decoration:none;transition:all .3s}.anchor span{color:#34495e}.anchor:hover{text-decoration:underline}.sidebar{border-right:1px solid rgba(0,0,0,.07);overflow-y:auto;padding:40px 0 0;position:absolute;top:0;bottom:0;left:0;transition:transform .25s ease-out;width:300px;z-index:20}.sidebar>h1{margin:0 auto 1rem;font-size:1.5rem;font-weight:300;text-align:center}.sidebar>h1 a{color:inherit;text-decoration:none}.sidebar>h1 .app-nav{display:block;position:static}.sidebar .sidebar-nav{line-height:2em;padding-bottom:40px}.sidebar li.collapse .app-sub-sidebar{display:none}.sidebar ul{margin:0 0 0 15px;padding:0}.sidebar li>p{font-weight:700;margin:0}.sidebar ul,.sidebar ul li{list-style:none}.sidebar ul li a{border-bottom:none;display:block}.sidebar ul li ul{padding-left:20px}.sidebar::-webkit-scrollbar{width:4px}.sidebar::-webkit-scrollbar-thumb{background:transparent;border-radius:4px}.sidebar:hover::-webkit-scrollbar-thumb{background:hsla(0,0%,53.3%,.4)}.sidebar:hover::-webkit-scrollbar-track{background:hsla(0,0%,53.3%,.1)}.sidebar-toggle{background-color:transparent;background-color:hsla(0,0%,100%,.8);border:0;outline:none;padding:10px;position:absolute;bottom:0;left:0;text-align:center;transition:opacity .3s;width:284px;z-index:30}.sidebar-toggle .sidebar-toggle-button:hover{opacity:.4}.sidebar-toggle span{background-color:var(--theme-color,#42b983);display:block;margin-bottom:4px;width:16px;height:2px}body.sticky .sidebar,body.sticky .sidebar-toggle{position:fixed}.content{padding-top:60px;position:absolute;top:0;right:0;bottom:0;left:300px;transition:left .25s ease}.markdown-section{margin:0 auto;max-width:800px;padding:30px 15px 40px;position:relative}.markdown-section>*{box-sizing:border-box;font-size:inherit}.markdown-section>:first-child{margin-top:0!important}.markdown-section hr{border:none;border-bottom:1px solid #eee;margin:2em 0}.markdown-section iframe{border:1px solid #eee;width:1px;min-width:100%}.markdown-section table{border-collapse:collapse;border-spacing:0;display:block;margin-bottom:1rem;overflow:auto;width:100%}.markdown-section th{font-weight:700}.markdown-section td,.markdown-section th{border:1px solid #ddd;padding:6px 13px}.markdown-section tr{border-top:1px solid #ccc}.markdown-section p.tip,.markdown-section tr:nth-child(2n){background-color:#f8f8f8}.markdown-section p.tip{border-bottom-right-radius:2px;border-left:4px solid #f66;border-top-right-radius:2px;margin:2em 0;padding:12px 24px 12px 30px;position:relative}.markdown-section p.tip:before{background-color:#f66;border-radius:100%;color:#fff;content:"!";font-family:Dosis,Source Sans Pro,Helvetica Neue,Arial,sans-serif;font-size:14px;font-weight:700;left:-12px;line-height:20px;position:absolute;height:20px;width:20px;text-align:center;top:14px}.markdown-section p.tip code{background-color:#efefef}.markdown-section p.tip em{color:#34495e}.markdown-section p.warn{background:rgba(66,185,131,.1);border-radius:2px;padding:1rem}.markdown-section ul.task-list>li{list-style-type:none}body.close .sidebar{transform:translateX(-300px)}body.close .sidebar-toggle{width:auto}body.close .content{left:0}@media print{.app-nav,.github-corner,.sidebar,.sidebar-toggle{display:none}}@media screen and (max-width:768px){.github-corner,.sidebar,.sidebar-toggle{position:fixed}.app-nav{margin-top:16px}.app-nav li ul{top:30px}main{height:auto;overflow-x:hidden}.sidebar{left:-300px;transition:transform .25s ease-out}.content{left:0;max-width:100vw;position:static;padding-top:20px;transition:transform .25s ease}.app-nav,.github-corner{transition:transform .25s ease-out}.sidebar-toggle{background-color:transparent;width:auto;padding:30px 30px 10px 10px}body.close .sidebar{transform:translateX(300px)}body.close .sidebar-toggle{background-color:hsla(0,0%,100%,.8);transition:background-color 1s;width:284px;padding:10px}body.close .content{transform:translateX(300px)}body.close .app-nav,body.close .github-corner{display:none}.github-corner:hover .octo-arm{-webkit-animation:none;animation:none}.github-corner .octo-arm{-webkit-animation:octocat-wave .56s ease-in-out;animation:octocat-wave .56s ease-in-out}}@-webkit-keyframes octocat-wave{0%,to{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@keyframes octocat-wave{0%,to{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}section.cover{align-items:center;background-position:50%;background-repeat:no-repeat;background-size:cover;height:100vh;display:none}section.cover.show{display:flex}section.cover.has-mask .mask{background-color:#fff;opacity:.8;position:absolute;top:0;height:100%;width:100%}section.cover .cover-main{flex:1;margin:-20px 16px 0;text-align:center;z-index:1}section.cover a{color:inherit}section.cover a,section.cover a:hover{text-decoration:none}section.cover p{line-height:1.5rem;margin:1em 0}section.cover h1{color:inherit;font-size:2.5rem;font-weight:300;margin:.625rem 0 2.5rem;position:relative;text-align:center}section.cover h1 a{display:block}section.cover h1 small{bottom:-.4375rem;font-size:1rem;position:absolute}section.cover blockquote{font-size:1.5rem;text-align:center}section.cover ul{line-height:1.8;list-style-type:none;margin:1em auto;max-width:500px;padding:0}section.cover .cover-main>p:last-child a{border-radius:2rem;border:1px solid var(--theme-color,#42b983);box-sizing:border-box;color:var(--theme-color,#42b983);display:inline-block;font-size:1.05rem;letter-spacing:.1rem;margin:.5rem 1rem;padding:.75em 2rem;text-decoration:none;transition:all .15s ease}section.cover .cover-main>p:last-child a:last-child{background-color:var(--theme-color,#42b983);color:#fff}section.cover .cover-main>p:last-child a:last-child:hover{color:inherit;opacity:.8}section.cover .cover-main>p:last-child a:hover{color:inherit}section.cover blockquote>p>a{border-bottom:2px solid var(--theme-color,#42b983);transition:color .3s}section.cover blockquote>p>a:hover{color:var(--theme-color,#42b983)}.sidebar,body{background-color:#fff}.sidebar{color:#364149}.sidebar li{margin:6px 0}.sidebar ul li a{color:#505d6b;font-size:14px;font-weight:400;overflow:hidden;text-decoration:none;text-overflow:ellipsis;white-space:nowrap}.sidebar ul li a:hover{text-decoration:underline}.sidebar ul li ul{padding:0}.sidebar ul li.active>a{border-right:2px solid;color:var(--theme-color,#42b983);font-weight:600}.app-sub-sidebar li:before{content:"-";padding-right:4px;float:left}.markdown-section h1,.markdown-section h2,.markdown-section h3,.markdown-section h4,.markdown-section strong{color:#2c3e50;font-weight:600}.markdown-section a{color:var(--theme-color,#42b983);font-weight:600}.markdown-section h1{font-size:2rem;margin:0 0 1rem}.markdown-section h2{font-size:1.75rem;margin:45px 0 .8rem}.markdown-section h3{font-size:1.5rem;margin:40px 0 .6rem}.markdown-section h4{font-size:1.25rem}.markdown-section h5{font-size:1rem}.markdown-section h6{color:#777;font-size:1rem}.markdown-section figure,.markdown-section p{margin:1.2em 0}.markdown-section ol,.markdown-section p,.markdown-section ul{line-height:1.6rem;word-spacing:.05rem}.markdown-section ol,.markdown-section ul{padding-left:1.5rem}.markdown-section blockquote{border-left:4px solid var(--theme-color,#42b983);color:#858585;margin:2em 0;padding-left:20px}.markdown-section blockquote p{font-weight:600;margin-left:0}.markdown-section iframe{margin:1em 0}.markdown-section em{color:#7f8c8d}.markdown-section code{border-radius:2px;color:#e96900;font-size:.8rem;margin:0 2px;padding:3px 5px;white-space:pre-wrap}.markdown-section code,.markdown-section pre{background-color:#f8f8f8;font-family:Roboto Mono,Monaco,courier,monospace}.markdown-section pre{-moz-osx-font-smoothing:initial;-webkit-font-smoothing:initial;line-height:1.5rem;margin:1.2em 0;overflow:auto;padding:0 1.4rem;position:relative;word-wrap:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#8e908c}.token.namespace{opacity:.7}.token.boolean,.token.number{color:#c76b29}.token.punctuation{color:#525252}.token.property{color:#c08b30}.token.tag{color:#2973b7}.token.string{color:var(--theme-color,#42b983)}.token.selector{color:#6679cc}.token.attr-name{color:#2973b7}.language-css .token.string,.style .token.string,.token.entity,.token.url{color:#22a2c9}.token.attr-value,.token.control,.token.directive,.token.unit{color:var(--theme-color,#42b983)}.token.function,.token.keyword{color:#e96900}.token.atrule,.token.regex,.token.statement{color:#22a2c9}.token.placeholder,.token.variable{color:#3d8fd1}.token.deleted{text-decoration:line-through}.token.inserted{border-bottom:1px dotted #202746;text-decoration:none}.token.italic{font-style:italic}.token.bold,.token.important{font-weight:700}.token.important{color:#c94922}.token.entity{cursor:help}.markdown-section pre>code{-moz-osx-font-smoothing:initial;-webkit-font-smoothing:initial;background-color:#f8f8f8;border-radius:2px;color:#525252;display:block;font-family:Roboto Mono,Monaco,courier,monospace;font-size:.8rem;line-height:inherit;margin:0 2px;max-width:inherit;overflow:inherit;padding:2.2em 5px;white-space:inherit}.markdown-section code:after,.markdown-section code:before{letter-spacing:.05rem}code .token{-moz-osx-font-smoothing:initial;-webkit-font-smoothing:initial;min-height:1.5rem;position:relative;left:auto}pre:after{color:#ccc;content:attr(data-lang);font-size:.6rem;font-weight:600;height:15px;line-height:15px;padding:5px 10px 0;position:absolute;right:0;text-align:right;top:0}
\ No newline at end of file
diff --git a/static/webp b/static/webp
deleted file mode 100644
index d467c04..0000000
Binary files a/static/webp and /dev/null differ