0%

我想,基本的技术功底已有认识设计模式的必要与能力,但

如今的我,不过是看到了cpp实现他们的方式,但对于问题模式的识别,这重中之重,则还需锤炼许多。

凭我的经验还不足以对这些设计模式都有深刻的了解,暂时我对这些模式没有那么多归纳的东西。

大概是要等到遇到某个问题,用了某个方法的时候,偶然才会有体会吧。

So i would call it, upcoming deja vu ~~

这是wiki与GOF读后感与笔记,很简短。

1. Creational Design Patterns

The creational patterns aim to separate a system from how its objects are created, composed, and represented. They increase the system’s flexibility in terms of the what, who, how, and when of object creation. Creational pattern - Wikipedia

1.1 Simple Factory

创建对象的代码的复用

何时使用: 仅仅是代码复用

1.2 Factory Method

创建对象的接口

助记名: Object Creation Interface

何时使用: 延迟创建的实现,需要提供抽象的接口时

1.3 Abstract Factory

gathering up relevant Factory Method into one packet

  • 助记名: Packet of Object Creation Interface

  • 何时使用:需要创建的多种对象之间有相关关系, 面向某一种更高层概念的Factory(例如对于创建 床+窗+天花板, 可以有面向更高层概念的抽象工厂: 创建暖色调、冷色调、保暖etc 的一组对象)

1.4 Builder

提供比其他的构造模式更加细致的创建过程的控制,本质还是代码复用

Simple Factory提供了代码复用的方式,但是纯传参控制的创建函数,在面对复杂的对象创建场景时会很麻烦(不然就只能提供超复杂的参数列表)

提供更细致的接口,可以提高创建过程的灵活性

Simple FactoyBuilder,只是我们提供了更具弹性的创建过程的控制方式

助记名: Detailed Controller of Object Creation

何时使用: 需要对对象创建过程提供更细节的控制

1.5 Prototype

通过复制原型的方式创建对象

助记名: Copy Constructor

何时使用: 对象初始化状态都是一样的, 省却可能的复杂的创建过程

1.6 Singleton

顾名思义,唯一的对象即为单例

这更多的是表明对象数量限制,其他的诸如创建过程当然随意,静态初始化也好,访问时初始化也好

何时使用: 当你认为该类型对象全局只该存在一个的时候

2. Structural Design Patterns

2.1 Adapter

将旧接口适配到新接口

参考别名: Wrapper

助记名: Interface Wrapper

2.2 Bridge @todo fix

Bridge 指连接两层抽象的节点(第二层抽象接口的指针,UML 中的Abstraction::this.impl). 当第二层抽象只有确定的实现的时候,Bridge 即为 Pimpl

UML description:

Bridge UML from wiki

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Abstraction (abstract class)

​ defines the abstract interface

​ maintains the Implementor reference.

RefinedAbstraction (normal class)

​ extends the interface defined by Abstraction

Implementor (interface)

​ defines the interface for implementation classes

ConcreteImplementor (normal class)

​ implements the Implementor interface

助记名: Two Layers Abstraction or Decoupled Abstraction & Implementation

何时使用:

  • Pimpl: 隐藏实现细节

  • 两层抽象,第二层与第一层的接口相互独立

2.3 Composite

将 一组对象视为单个对象一样操作

何时使用: 当前操作如果可以忽略操作的对象是单个对象还是一组对象的话,可以抽象出一致性的接口来

助记名: Treat Ones as one

何时使用:当前操作逻辑忽略操作对象是个独立对象还是一组对象

2.4 Decorator @todo fix

在同样的接口上提供额外的功能

尽管我们可以在这种模式下操作更多,比如直接跳过源对象的行为,但这不是一种好的设计。这种模式对于实现单一职责很有帮助(运行时线性职责组合避免静态功能组合的爆炸)

助记名: Single Responsiblity Helper:smirk:

何时使用: 太过复杂 [wiki](Decorator pattern - Wikipedia)

2.5 Facade

复杂子系统的简单接口

助记名: 表面

2.6 Flyweight

可共享的不可变对象

1
Anything that will be cached is flyweight.

助记名: Shareable Invariant

何时使用:在某类对象的某些属性可以共享的情况下,可以剥离出来,减少内存占用

2.7 Proxy

添加对原始对象的控制逻辑,在原始的对象使用过程中插入自定义逻辑

实现方式多和 Adapter 类似,只是目的不同

image from wiki

3. Behavioral Design Patterns

3.1 Chain-of-responsibility

  • Coupling the sender of a request to its receiver should be avoided.

  • It should be possible that more than one receiver can handle a request.

3.2 Command

解耦请求的触发与请求的执行,所谓 avoid hard-wired requests

上层只需关注请求的触发,所谓 Invoke a Command, 而无需关注请求的执行

通过场景来理解:

  1. GUI button 相关的Action执行。Action就是一个Command对象,UI触发Action Command的执行
  2. 宏录制(Macro recording). 如果所有用户行为都被表示为 Command,则所谓录制用户一系列行为就是记录一系列的Command对象
  3. 多级回退(Multi-level undo). 比2更进一步的,Command对象如果有undo方法,那么多级回退就是Command对象的undo调用与出栈

当解耦请求的执行后,仅仅关注简单的请求触发(Invoke command),那么可以玩的很花,不是吗

700px-Command_pattern.svg.png (700×230) (wikimedia.org)

3.3 Interpreter

wiki介绍解释器全文用的是针对语言解析的例子。设计模式解释动机时,描述的较为理想:如果一个特定类型的问题发生的频率足够高,那么就值得将该问题的各个实例表述为一个简单语言中的句子。这样就可以构建一个解释器,该解释器通过解决这些句子来解决该问题。

核心就是找到问题域的可重入模式,构建层级的AST,简化问题的解决

3.4 Iterator

抽象容器访问接口,隐藏内部元素组织,解耦算法与容器。

想想 std::sort 用于vector<int> 与 int[] 这种随机访问容器时的迭代器即可知

3.5 Mediator

mediator对象负责处理多个对象间的复杂交互

  • Air Traffic Control: In a simulation or software for air traffic control, a mediator might manage the communication between different aircraft, ground control, and other relevant systems. It ensures that messages are properly routed and that conflicts are resolved.
  • Online Chat Systems: In a chat application, a mediator could manage the communication between different users in a chat room. It ensures that messages are delivered to the correct recipients and handles actions like joining or leaving the chat.
  • Multiplayer Games: In a multiplayer game, a mediator might coordinate communication between different players, game objects, and the server. It ensures that actions performed by one player are properly communicated to others and that the game state remains synchronized.

3.6 Memento

Memento 记录对象的内部状态,于是对象可以回到之前的状态

是一种特殊的接口,让对象支持状态备份与恢复

Command undo 也可以实现状态恢复,不过Memento显然更加直截了当

应用领域:

  1. Text editors and IDEs: Implementing undo/redo functionality.
  2. Games: Managing game states and allowing players to revert to previous states.
  3. Transaction management: Handling rollback mechanisms in databases or distributed systems.
  4. User interfaces: Saving and restoring the state of complex UI components.
  5. Configuration management: Storing and restoring the configuration of an application.

3.7 Observer

  • A one-to-many dependency between objects should be defined without making the objects tightly coupled.
  • When one object changes state, an open-ended number of dependent objects should be updated automatically.
  • An object can notify multiple other objects.

例子:

  1. Stock Market: In a financial application monitoring stock prices, various components might observe changes in the prices of specific stocks. When a stock price changes, the observers (e.g., components displaying the prices) are notified and update accordingly.
  2. Weather Monitoring System: In a weather monitoring system, different components might observe changes in temperature, humidity, pressure, etc. When any of these parameters change, the observers are notified, and relevant actions can be taken (e.g., displaying an alert).
  3. UI Components in MVC: In the Model-View-Controller (MVC) architectural pattern, views observe changes in the model and update themselves accordingly. For example, in a web application, a view might observe changes in the underlying data model and update the displayed content.
  4. Distributed Systems Communication: In a distributed system where different components need to be notified of changes or events, the Observer pattern can be used to facilitate communication. For example, in a publish-subscribe messaging system, subscribers observe topics and receive notifications when messages are published.
  5. Chat Applications: In a chat application, users observe changes in the chat room or conversation they are participating in. When new messages are sent or received, observers are notified, and the UI updates to display the new messages.

3.8 State

W3sDesign_State_Design_Pattern_UML.jpg (600×240) (wikimedia.org)

Different State Imple is going to gathering the related behaviours.

This seems a little heavy. But this is asuring simplity of imple of new state, otherwise there would be a prerequestion for previous code.

The state pattern is set to solve two main problems:[4]

  • An object should change its behavior when its internal state changes.
  • State-specific behavior should be defined independently. That is, adding new states should not affect the behavior of existing states.

The State Transition requires The Context Object, which holds reference of The State Objects, need be notified when transition required.

3.9 Strategy

Similar with The State, Strategy encapsulate the behaviours into separate derived imple, to keep the calling code reusability.

3.10 Template method

Template method pattern - Wikipedia

1
The template method is a method in a superclass, usually an abstract superclass, and defines the skeleton of an operation in terms of a number of high-level steps. These steps are themselves implemented by additional *helper methods* in the same class as the *template method*. 

3.11 Visitor

Vistor与双分派 - lullaby (cheerlisten.com)

3.A1 Fluent interface

fluent interface is API style. Be Like

​ change the code from

1
2
3
4
5
6
7

GlutApp app(argc, argv);
app.setDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_ALPHA|GLUT_DEPTH); // Set framebuffer params
app.setWindowSize(500, 500); // Set window params
app.setWindowPosition(200, 200);
app.setTitle("My OpenGL/GLUT App");
app.create();

​ to

1
2
3
4
5
FluentGlutApp(argc, argv)
.withDoubleBuffer().withRGBA().withAlpha().withDepth()
.at(200, 200).across(500, 500)
.named("My OpenGL/GLUT App")
.create();

3.A2 Specification

Specification_UML.png (541×323) (wikimedia.org)

抽象业务逻辑过滤的规则: ISpecification

并通过一组CompositeSpecification支持规则组合

Reference

GitHub - kamranahmedse/design-patterns-for-humans: An ultra-simplified explanation to design patterns

Using SVID2/XPG interface to check heap memory.

before all: The GNU Allocator (The GNU C Library)

read this for a outline for gnu c malloc.

mallinfo

arena + hblkhd is the total size of malloced mem. More detailed, arena is the size of all heaps in all arenas, while hblkhd is the size of all mmapped regions.

fordblks is the key data for check memory fragmentation, this stands for size of all free chunks.

to check after a routine, if there got more allocated space, check arena + hblkhd

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* malloc.h */
struct mallinfo
{
int arena; /* non-mmapped space allocated from system */
int ordblks; /* number of free chunks */
int smblks; /* number of fastbin blocks */
int hblks; /* number of mmapped regions */
int hblkhd; /* space in mmapped regions */
int usmblks; /* always 0, preserved for backwards compatibility */
int fsmblks; /* space available in freed fastbin blocks */
int uordblks; /* total allocated space */
int fordblks; /* total free space */
int keepcost; /* top-most, releasable (via malloc_trim) space */
};

Malloc Tunable Parameters

1
2
3
4
5
6
7
8
9
10
11
/* mallopt options that actually do something */
#define M_TRIM_THRESHOLD -1
#define M_TOP_PAD -2
#define M_MMAP_THRESHOLD -3
#define M_MMAP_MAX -4
#define M_CHECK_ACTION -5
#define M_PERTURB -6
#define M_ARENA_TEST -7
#define M_ARENA_MAX -8
/* General SVID/XPG interface to tunable parameters. */
extern int mallopt (int __param, int __val) __THROW;

Appendix

Reference

memory fragmentation

the most simple way is to limit the arena count

The M_AREANA_MAX,

This parameter sets the number of arenas to use regardless of the number of cores in the system.

The default value of this tunable is 0, meaning that the limit on the number of arenas is determined by the number of CPU cores online. For 32-bit systems the limit is twice the number of cores online and on 64-bit systems, it is eight times the number of cores online. Note that the default value is not derived from the default value of M_ARENA_TEST and is computed independently.

This parameter can also be set for the process at startup by setting the environment variable MALLOC_ARENA_MAX to the desired value.

If you known what you are doing, change ``M_MMAP_THRESHOLDshould also works. Since page size on x86_64 Linux are 4096(check from commands$getconf PAGESIZE` ), all value greater or equal to this would make sense.

Code to understand mallinfo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <malloc.h>

size_t heap_malloc_total, heap_free_total, mmap_total, mmap_count;

void print_info()
{
struct mallinfo mi = mallinfo();
printf("count by itself:\n");
printf("\theap_malloc_total=%lu heap_free_total=%lu heap_in_use=%lu\n\tmmap_total=%lu mmap_count=%lu\n",
heap_malloc_total * 1024, heap_free_total * 1024, heap_malloc_total * 1024 - heap_free_total * 1024,
mmap_total * 1024, mmap_count);
printf("count by mallinfo:\n");
printf("\theap_malloc_total=%lu heap_free_total=%lu heap_in_use=%lu\n\tmmap_total=%lu mmap_count=%u\n", mi.arena,
mi.fordblks, mi.uordblks, mi.hblkhd, mi.hblks);
printf("from malloc_stats:\n");
malloc_stats();
}

#define ARRAY_SIZE 200
int main(int argc, char **argv)
{
char *ptr_arr[ARRAY_SIZE];
int i;
for (i = 0; i < ARRAY_SIZE; i++)
{
ptr_arr[i] = (char *)malloc(i * 1024);
if (i < 128) // glibc默认128k以上使用mmap
{
heap_malloc_total += i;
}
else
{
mmap_total += i;
mmap_count++;
}
}
print_info();

for (i = 0; i < ARRAY_SIZE; i++)
{
if (i % 2 == 0)
continue;
free(ptr_arr[i]);

if (i < 128)
{
heap_free_total += i;
}
else
{
mmap_total -= i;
mmap_count--;
}
}

printf("\nafter free\n");
print_info();

return 1;
}

see build_ffmpeg on linux for how to build ffmpeg easily

1
2
3
4
5
export LD_LIBRARY_PATH=~/code//ffmpeg/install/lib:.
cd ~/code//ffmpeg/install/bin

# run
./ffplay -i /e/RES/Videos/h2645/sample-An.mp4 -threads 1 -filter_threads 1

threads

  • ffplay: main thread, sleeping
  • SDLTimer: some Timer stuffs
  • PulseHotPlug: pulse audio thread
  • read_thread: demuxer thread
  • video_decoder: decoder thread

video decode process

read_thread:

1
2
ret = av_read_frame(ic, pkt);
packet_queue_put(&is->videoq, pkt);

video_decoder:

1
2
3
4
5
6
7
8
9
10
11
// keep try get_video_frame
// change order of showup of avcodec_send_packet & avcodec_receive_frame for better comprehension
get_video_frame(){
decoder_decode_frame(){
if (packet_queue_get(d->queue, d->pkt, 1, &d->pkt_serial) < 0)
return -1;
avcodec_send_packet(d->avctx, d->pkt);
avcodec_receive_frame(d->avctx, frame);
}
queue_picture(is, frame, pts, duration, fd ? fd->pkt_pos : -1, is->viddec.pkt_serial);
}

buffer control

read_thread: control the buffer size of stream data

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#define MAX_QUEUE_SIZE (15 * 1024 * 1024)
// size is counted in bytes
/* if the queue are full, no need to read more */
if (infinite_buffer<1 &&
(is->audioq.size + is->videoq.size + is->subtitleq.size > MAX_QUEUE_SIZE
|| (stream_has_enough_packets(is->audio_st, is->audio_stream, &is->audioq) &&
stream_has_enough_packets(is->video_st, is->video_stream, &is->videoq) &&
stream_has_enough_packets(is->subtitle_st, is->subtitle_stream, &is->subtitleq)))) {
/* wait 10 ms */
SDL_LockMutex(wait_mutex);
SDL_CondWaitTimeout(is->continue_read_thread, wait_mutex, 10);
SDL_UnlockMutex(wait_mutex);
continue;
}

video_decoder: keep pulling data queued from read_thread, and stop when queued pictures size exceeds

note the value of VIDEO_PICTURE_QUEUE_SIZE

1
2
3
4
5
6
7
8
9
#define VIDEO_PICTURE_QUEUE_SIZE 3
#define SUBPICTURE_QUEUE_SIZE 16
#define SAMPLE_QUEUE_SIZE 9
video_thread::queue_picture(){
frame_queue_peek_writable(){
while (f->size >= f->max_size && !f->pktq->abort_request)
SDL_CondWait(f->cond, f->mutex);
}
}

seek

the key is the concept serial, of both PacketQueue::serial, MyAVPacketList::serial in PacketQueue list, and Decoder::pkt_serial.

ffplay use serial to syn its state.

like when seek happens, the PacketQueue change it serial, then the demuxed packets, finally the Decoder object.

for the seek:

  1. :arrow_left: Left-Arrow Keydown
  2. ffpaly: in SDL event_loop: set {seek_req, seek_flag}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
event_loop(){
case SDLK_LEFT:
incr = seek_interval ? -seek_interval : -10.0;
goto do_seek;
...
do_seek:
stream_seek(cur_stream, pos, incr, 1);
}

/* seek in the stream */
static void stream_seek(VideoState *is, int64_t pos, int64_t rel, int by_bytes)
{
// here this seek_req means a unfinished seek request
if (!is->seek_req) {
is->seek_pos = pos;
is->seek_rel = rel;
is->seek_flags &= ~AVSEEK_FLAG_BYTE;
if (by_bytes)
is->seek_flags |= AVSEEK_FLAG_BYTE;
is->seek_req = 1;
SDL_CondSignal(is->continue_read_thread);
}
}
  1. read_thread: seek demuxer and flush packet_buffer

​ note serial is incre by 1 in packet_queue_flush

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
static int read_thread(void *arg){
for(;;){
if (is->seek_req) {
ret = avformat_seek_file(is->ic, -1, seek_min, seek_target, seek_max, is->seek_flags);
packet_queue_flush(&is->videoq);
}
}
}

static void packet_queue_flush(PacketQueue *q)
{
MyAVPacketList pkt1;

SDL_LockMutex(q->mutex);
while (av_fifo_read(q->pkt_list, &pkt1, 1) >= 0)
av_packet_free(&pkt1.pkt);
q->nb_packets = 0;
q->size = 0;
q->duration = 0;
q->serial++; // propagate to packet serial in packet_queue_put_private
SDL_UnlockMutex(q->mutex);
}

static int packet_queue_put_private(PacketQueue *q, AVPacket *pkt)
{
...
pkt1.serial = q->serial;
...
}
  1. video_decoder: avcodec_flush_buffers when serials unmatch
1
2
3
4
5
6
7
8
9
10
11
12
13
static int decoder_decode_frame(Decoder *d, AVFrame *frame, AVSubtitle *sub) {
do{
int old_serial = d->pkt_serial;
if (packet_queue_get(d->queue, d->pkt, 1, &d->pkt_serial) < 0)
return -1;
if (old_serial != d->pkt_serial) {
avcodec_flush_buffers(d->avctx);
d->finished = 0;
d->next_pts = d->start_pts;
d->next_pts_tb = d->start_pts_tb;
}
} while (1);
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#!/bin/bash

# Initialize the timestamp variable with the current date and time
tsf=$(mktemp)
echo $(date -u -d "2024-04-09 23:44:24" +%s) >$tsf
echo "tsf=$tsf"

# Function to prepend text to a file
prepend() {
local tempFile
tempFile=$(mktemp)
echo "$2" > "$tempFile"
cat "$1" >> "$tempFile"
mv "$tempFile" "$1"
}

# Function to generate the content to be prepended
generate_content() {
filename=$(basename "$1" .md)

# Increment the timestamp by one second
ts=$(($(cat $tsf) - 1))

# Convert the timestamp to the date format
date=$(date -u -d "@$ts" +"%Y-%m-%d %T")
echo $ts >$tsf
echo "---
title: $filename
categories:
- [jackfrued-Python-temp]
date: $date
draft: false
tags:
---
"
}

CWD="$(pwd)"

cd source/_posts/Python-100-Days
git reset --hard
rm -r res
rm -r "公开课"
rm -r "番外篇"
rm *.md
# Main script
for file in $(find . -name "*.md"|grep Day |sort); do
content=$(generate_content "$file")
prepend "$file" "$content"
done

标准相关节

  • Table 9-34 – Syntax elements and associated types of binarization, maxBinIdxCtx, and ctxIdxOffset
  • Table 9-39 - the assignment of ctxIdx increments (ctxIdxInc) to binIdx for all ctxIdxOffset
  • Table 9-36 – Binarization for macroblock types in I slices
  • 9.3.3.1.1.3 Derivation process of ctxIdxInc for the syntax element mb_type

主流程

  • Table 9-34 查询相关上下文范围= [ctxIdxOffset = 3,10]
  • Table 9-39 查询知各binIdx对应的ctxIdxOffsetInc的计算方法
  • Table 9-36 查询知mb_type 的二值化表,这是顺序解析bins的指引

各个bin的 ctxIdx = ctxIdxOffset +ctxIdxInc

bin_0

由 Table 9-39 进入 Spec 9.3.3.1.1.3

前置知识: 邻宏块ABCD位置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
9.3.3.1.1.3 Derivation process of ctxIdxInc for the syntax element mb_type

Input to this process is ctxIdxOffset.

Output of this process is ctxIdxInc.

The derivation process for neighbouring macroblocks specified in clause 6.4.11.1 is invoked and the output is assigned to mbAddrA and mbAddrB.

Let the variable condTermFlagN (with N being either A or B) be derived as follows:

–If any of the following conditions are true, condTermFlagN is set equal to 0:

–mbAddrN is not available,

–ctxIdxOffset is equal to 0 and mb_type for the macroblock mbAddrN is equal to SI,

–ctxIdxOffset is equal to 3 and mb_type for the macroblock mbAddrN is equal to I_NxN,

–ctxIdxOffset is equal to 27 and mb_type for the macroblock mbAddrN is equal to B_Skip or B_Direct_16x16.

–Otherwise, condTermFlagN is set equal to 1.

The variable ctxIdxInc is derived as

Image

计算得到 ctxIdxInc0

bin_1

Table 9-36 标识的所谓 ctxIdx=276 意味着这一个bin使用 二进制DecodeTerminate解码,见 Spec 9.3.3.2 Arithmetic decoding process

bin_{n>1}

对于binIdx = [2, 5 or 6],解码第三、四个比特分别使用上下文 ctxIdxOffset + 3、 ctxIdxOffset + 4

第4bit和第5bit上下文依赖于第3bit,见 Spec 9.3.3.1.2(依赖输入ctxIdxOffset + binIdx)

这里的结果比较有趣,结合 Table 9-36的表可以得到结果,无视第5个bit的上下文偏移增量的计算,只需要在bin_3为1时,插入对 ctxIdxInc=5 的bin解析就行,于是产生 [ctxIdxInc0, 276, 3, 4, 5, 6, 7], 或者 [ctxIdxInc0, 276, 3, 4, 6, 7]的上下文索引增量序列

code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
int decode_mb_type_cabac(Macroblock_t* curMB)
{
Slice_t* slice = curMB->slice;

const int ctxIdxOffset = 3;
int ctxInc0 = ctxIdxOffset;
if (slice->mbTypeA & (MT_INTRA16x16 | MT_INTRA_PCM))
ctxInc0++;
if (slice->mbTypeB & (MT_INTRA16x16 | MT_INTRA_PCM))
ctxInc0++;

if (0 == slice->cabac.decoder.DecodeDecision(slice->cabac.bac_contexts + ctxInc0))
return 0; // I_NxN

if (slice->cabac.decoder.DecodeTermination())
return 25; // I_PCM

int ret = 1;
if (slice->cabac.decoder.DecodeDecision(slice->cabac.bac_contexts + ctxIdxOffset + 3))
ret += 12;
if (slice->cabac.decoder.DecodeDecision(slice->cabac.bac_contexts + ctxIdxOffset + 4))
{
ret += 4;
if (slice->cabac.decoder.DecodeDecision(slice->cabac.bac_contexts + ctxIdxOffset + 5))
ret += 4;
}
if (slice->cabac.decoder.DecodeDecision(slice->cabac.bac_contexts + ctxIdxOffset + 6))
ret += 2;
if (slice->cabac.decoder.DecodeDecision(slice->cabac.bac_contexts + ctxIdxOffset + 7))
ret += 1;

return ret;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#!/bin/bash
CWD=$(pwd)
INSTALL_PATH=$CWD/install

check_repo(){
woring_dir=$1
dir=$2
required_branch=$3
repo_url=$4
cd $woring_dir

if [[ ! -d $dir ]];then
git clone $repo_url $dir
fi

if [[ "$required_branch" != "" ]]; then
echo "
on $dir"
cd $dir
git checkout $required_branch
git reset --hard
fi
cd $CWD
}

check_www(){
woring_dir=$1
dir=$2
www_file=$3
www_url=$4
shift 4
if_custom_commands=$@
cd $woring_dir

if [[ ! -f $www_file ]];then
wget $www_url $www_file
fi

if [[ ! -d $dir ]]; then
if [[ "$if_custom_commands" == "" ]]; then
tar -xvf $www_file
else
$@
fi
fi
cd $CWD
}

active_target=
set_target(){
active_target=$1
echo "
activating $active_target"
}
check(){
$@ >$CWD/build/logs/$active_target.log 2>$CWD/build/logs/$active_target.error.log
retval=$?
if [[ "$retval" != "0" ]];then
echo "target $active_target: failed retval=$retval $@"
exit $retval
fi
}

##################################################
# 准备目录 #
mkdir -p $CWD/build/logs 2>/dev/null
rm $CWD/build/logs/* 2>/dev/null
mkdir -p 3rd 2>/dev/null


###################################################
# 第三方库 #

# check_repo 3rd/x264 master https://code.videolan.org/videolan/x264.git
check_repo 3rd libvpx main https://chromium.googlesource.com/webm/libvpx
check_www 3rd lame-3.100 lame-3.100.tar.gz https://zenlayer.dl.sourceforge.net/project/lame/lame/3.100/lame-3.100.tar.gz

set_target libvpx
cd 3rd/libvpx
check ./configure --prefix=$INSTALL_PATH --enable-vp8 --enable-vp9 --enable-pic --disable-examples --disable-docs --enable-vp9-highbitdepth
check make install -j
cd $CWD


set_target lame-3.100
cd 3rd/lame-3.100
check ./configure --prefix=$INSTALL_PATH --enable-shared=no
check make install -j
cd $CWD

##################################################
# ffmpeg #
check_repo . ffmpeg n6.1 https://github.com/FFmpeg/FFmpeg.git

set_target ffmpeg
export PKG_CONFIG_PATH=$CWD/install/lib/pkgconfig
cd ffmpeg
check ./configure \
--prefix=$INSTALL_PATH \
--extra-cflags=-I$CWD/install/include \
--extra-ldflags=-L$CWD/install/lib \
--enable-libvpx \
--enable-libmp3lame
check make install -j
cd $CWD

docker installation

1
2
3
4
5
6
7
8
9
10
11
12
13
for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done

# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
# Add the repository to Apt sources:
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

user config

1
usermod -aG docker lull

pull alpine

even if docker hub shows no alpine 3.15

1
docker pull alpine:3.15

create container

  • -i: keep STDIN, 否则 docker start 直接退出,docker exec 也无效,非交互模式应该是在启动脚本里加入long live server
  • -t: 创建伪终端,更好的cli交互体验
  • –name alpine: 指定名字
  • –publish-all: 公开端口
  • -v /e:/e 共享挂载目录
  • 1
    docker run -it --name alpine --publish-all -v /e:/e alpine:3.15

run

start container if not yet

1
docker ps  |grep alpine || docker start alpine

run interactive ali

1
docker exec -it  alpine sh

Appendix

running ssh service in these containers is a bit annoying

although you could just run

1
2
apk add openssh
`which sshd`

外部流量入口

此处port=10001 可以切换成其他端口

然后所有这类流量先通过 dokodemo-door 通通传入38001

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"inbounds": [
{
"listen": "0.0.0.0",
"port": 10001,
"protocol": "dokodemo-door",
"settings": {
"address": "127.0.0.1",
"port": 38001,
"network": "tcp",
"followRedirect": false
}
}
]
}

分流

此处fallbacks描述分流规则

  • 默认回落流量到31296,一般无法解析的流量会分发给nginx,以隐藏xray的响应特征
  • 满足TLS ALPN协商的走到31302
  • 尝试匹配首包 HTTP PATH(installpathws or installpathws2),成功走到31297 | 31299
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
{
"inbounds": [
{
"port": 38001,
"fallbacks": [
{
"dest": 31296,
"xver": 1
},
{
"alpn": "h2",
"dest": 31302,
"xver": 0
},
{
"path": "/installpathws",
"dest": 31297,
"xver": 1
},
{
"path": "/installpathws2",
"dest": 31299,
"xver": 1
}
]
}
...
]
}

响应端口

示例为vless
streamSettings.wsSettings.path 对应 分流回落的fallbacks.path
inbounds.listen 对应输入端口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
{
"inbounds": [
{
"port": 31297,
"listen": "127.0.0.1",
"protocol": "vless",
"tag": "VLESSWS",
"settings": {
"clients": [
{
"id": "uuid-idxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"email": "username_xxxx-VLESS_WS"
}
],
"decryption": "none"
},
"streamSettings": {
"network": "ws",
"security": "none",
"wsSettings": {
"acceptProxyProtocol": true,
"path": "/installpathws"
}
},
"sniffing": {
"enabled": true,
"destOverride": [
"http",
"tls"
]
}
}
]
}

reference

最近重装了服务器,记录以下重装的操作

创建用户lull

1
2
3
4
sudo su
adduser lull
# 添加到sudo组以使用sudo命令,就不要手动更改sudoers了
usermod -aG sudo lull

配置SSH登录选项

1
2
3
4
5
6
7
8
9
mkdir ~/.ssh
# 查看本地公钥 cat ~/.ssh/id_rsa.pub
echo "xxxxx
" >> ~/.ssh/authorized_keys

# 禁止密码登录
# vim /etc/sshd_config
# change properties:
# PasswordAuthentication no

xrrraay

1
2
3
4
5
6
7
8
9
10
11
12
13
# 安装nginx
apt install nginx
# 安装xray
bash -c "$(curl -L https://github.com/XTLS/Xray-install/raw/main/install-release.sh)" @ install
# xray默认用户nobody污染权限
chmod 777 /var/log/xray -r

## 更改xray服务配置 /etc/systemd/system/xray.service.d/10-donot_touch_single_conf.conf
## 添加xray出入口配置文件
## 更改nginx 配置文件

systemctl enable nginx.service
systemctl enable xray.service

打开BBR

https://wiki.crowncloud.net/?How_to_enable_BBR_on_Ubuntu_24_04
BBR拥塞控制算法 数据,BBR在丢包存在时,相对默认拥塞控制的传输速度要快很多

/etc/sysctl.conf 追加以下内容,然后 重载配置: sysctl -p,通过 lsmod |grep bbr 查看bbr 是否开启

1
2
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr

node (非系统版本)

nvm 是node版本管理工具

1
2
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh | bash
nvm install --lts

文档服务器:编译

1
2
3
4
5
6
7
8
9
10
11
sudo mkdir /code
sudo chmod 777 /code

git clone git@github.com:September007/doc-site-hexo.git
cd doc-site-hexo
git submodule update --init --recursive

npm install -g hexo
npm i
hexo g;hexo g; hexo g;

文档服务器:部署

certbot生成证书

1
2
3
4
5
6
7
sudo apt instal certbot
sudo certbot certonly --standalone -d www.cheerlisten.com
# Successfully received certificate.
# Certificate is saved at: /etc/letsencrypt/live/www.cheerlisten.com/fullchain.pem
# Key is saved at: /etc/letsencrypt/live/www.cheerlisten.com/privkey.pem
# This certificate expires on 2024-03-02.
# These files will be updated when the certificate renews.

服务器配置 /etc/nginx/conf.d/all/doc-server.conf
nginx 服务器配置
sudo ln -s /etc/nginx/conf.d/all/doc-server.conf /etc/nginx/conf.d/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
server {
listen 443 ssl;
server_name www.cheerlisten.com;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_certificate /etc/letsencrypt/live/www.cheerlisten.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/www.cheerlisten.com/privkey.pem; # managed by Certbot

location / {
root /code/doc-site-hexo/public/;
index index.html tags/index.xml index.xml;
}
location /doc/ {
alias /code/doc-site-hexo/public/;
index tags/index.html tags/index.xml index.html index.xml;
}

#error_page 404 /404.html;

# redirect server error pages to the static page /50x.html
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}

杂项

更改设备名

1
sudo echo "LiteServer-ALiYun" > /etc/hostname

fix certbot dep urllib error: pip install -U 'requests-toolbelt>=1'

1
2
3
4
5
6
7
8
9
10
11
# 更新当前包
sudo apt update; sudo apt upgrade -y
sudo apt dist-upgrade
sudo apt autoremove

sudo apt install update-manager-core

# reboot

sudo do-release-upgrade --check-dist-upgrade-only -d
sudo do-release-upgrade -d