( ˘ ³˘)♥ 这里是Lei的博客。
macOS Tahoe 提取苹方字体(OTF / TTF)
本文记录如何在 macOS Tahoe(macOS 26) 上提取系统自带的苹方(PingFang)字体,并分别导出: 原始 OTF(无损) TrueType TTF(通过 FontTools + cu2qu 转换) 一、系统环境 适用于: macOS Tahoe(macOS 26) Apple Silicon(M 系列)或 Intel Mac Tahoe 开始,苹方字体不再位于传统的: 1 /System/Library/Fonts/ 而是作为系统资源(Asset)存放在: 1 /System/Library/AssetsV2/ 二、查找系统中的 PingFang.ttc 直接搜索: 1 find /System/Library/AssetsV2 -name "PingFang*.ttc" 或者: 1 find /System /Library -name "PingFang*.ttc" 2>/dev/null 通常会得到类似结果: 1 /System/Library/AssetsV2/.../AssetData/PingFang.ttc 复制出来即可: 1 cp "/System/Library/AssetsV2/.../PingFang.ttc" . 建议先复制到当前目录,再进行后续操作。 三、导出原始 OTF(无损) 系统中的 PingFang.ttc 实际上是 OpenType Collection。 其中每个成员都是: CFF OpenType(OTF) 而不是: ...
记录常用的各种工具
用于Go项目 名称 描述 gopkgview 交互式的基于Web的Go包依赖图可视化 gup 更新由go install安装的二进制文件 gsa 分析编译后的Go二进制文件大小的工具,可以清晰的了解每个包或依赖在二进制中占用的大小 mockgen gomock是Go编程语言的模拟框架 nilaway 静态分析工具,用于检测Go代码中的潜在nil恐慌 命令行 名称 描述 bat cat的现代替代品 curlie curl的力量,httpie的使用便捷性 croc 轻松安全地将内容从一台计算机发送到另一台计算机 difft 结构化差异,理解语法。也可用于git diff direnv 加载或卸载当前目录(以及目录)的.envrc或.env fastfetch 功能丰富、面向性能、类似 neofetch 的系统信息工具 fd find的现代替代品 fzf 命令行模糊查找器 grpcurl 与cURL类似,但针对gRPC:与gRPC服务器交互的命令行工具 htop top的替代品。一个交互式流程查看器 hyperfine 命令行基准测试工具 jq 命令行JSON处理器 lsd ls的现代替代品 mycli MySQL自动补全和语法高亮的终端客户端 ouch 支持多种格式的压缩和解压缩,tar、zip、7z、gz、rar等 pgcli Postgres自动补全和语法高亮的终端客户端 rg 一个面向行的搜索工具,它递归地搜索当前目录中的正则表达式模式 scc 一个类似于cloc、sloccount和tokei的工具。用于统计多种编程语言的代码行数、空白行、注释行和源代码的物理行数 sd sed的现代替代品 vivid 具有丰富文件类型数据库的主题化LS_COLORS生成器 zoxide 更智能的cd命令,支持jump App 名称 描述 iina 适用于macOS的现代视频播放器 mac-mouse-fix macOS平滑滚动
十亿行挑战
十亿行挑战(1️⃣🐝🏎️ The One Billion Row Challenge) 原始仓库 文中源码仓库: https://github.com/zzhaolei/1brc 目标 文本文件包含了一系列气象站的温度值。每行是一个测量值,格式为<string: station name>;<double: measurement>,其中测量值精确到一位小数。以下是一些示例行: 1 2 3 4 5 6 7 8 9 10 Hamburg;12.0 Bulawayo;8.9 Palembang;38.8 St. John's;15.2 Cracow;12.6 Bridgetown;26.9 Istanbul;6.2 Roseau;34.4 Conakry;31.2 Istanbul;23.0 任务是编写一个程序,该程序读取文本文件,计算每个气象站的最低、平均和最高温度值,并将结果输出到stdout, 格式如下(按气象站名称字母顺序排序,并且每个气象站的结果值格式为<min>/<mean>/<max>,保留一位小数点): 1 {Abha=-23.0/18.0/59.2, Abidjan=-16.2/26.0/67.3, Abéché=-10.0/29.4/69.0, Accra=-10.1/26.4/66.4, Addis Ababa=-23.7/16.0/67.0, Adelaide=-27.8/17.3/58.5, ...} 限制 只能使用标准库实现。 生成十亿行挑战所需的数据 克隆原始仓库: 1 2 3 git clone https://github.com/gunnarmorling/1brc cd 1brc/src/main/python python3 create_measurements.py 1000000000 生成的数据会在1brc/measurements.txt,约为15Gi的大小。 ...
Go 在 Mac 或 Linux 上构建动态库
Go 可以导出 C ABI,然后在其它兼容 C ABI 的语言中调用。 下面详细讲解一下用法: Go 构建动态库 定义一个 go 文件,包含以下代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package main import "C" //export Add func Add(a, b int) int { return a + b } //export Multiply func Multiply(a, b int) int { return a * b } func main() {} go 的文件名称在当前示例中无关紧要,这里定义为 main.go。 ...
Rust main 函数是如何被执行的
Rust的main函数到底是如何被执行的呢? 让我们看一个关于main函数的示例: 1 2 3 4 5 6 use std::error::Error; fn main() -> Result<(), Box<dyn Error>> { println!("hello world"); Ok(()) } 从这个示例我们可以看到,rust的main函数竟然还可以返回Result枚举,这是为什么?rust到底是如何执行用户定义的main函数的呢? 接下来让我们对rust的源码进行剖析,看一看rust到底是如何运行main函数的。 Rust 运行时 首先,在几乎所有的语言中(目前我不知道哪个语言会不进行处理),在执行用户的main函数之前都需要进行一些初始化工作,比如分配堆栈、创建并绑定主线程、初始化通用寄存器、初始化GC等等。 而rust也不例外,也会在实际调用用户执行的main之前进行一些初始化的操作。 你没看错,rust也是有运行时的,只不过这个运行时没有GC,非常的轻量级,主要是执行上面所说的初始化操作以及对main函数的执行和收尾。 让我们先从init开始: 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 // 在执行 main 之前执行 unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { #[cfg_attr(target_os = "teeos", allow(unused_unsafe))] unsafe { // 实际的资源初始化逻辑 sys::init(argc, argv, sigpipe) }; // 设置主线程,并设置一个名字 let thread = Thread::new_main(); thread::set_current(thread); } // 运行时只会执行一次 cleanup。 // 在 main 或程序退出的时候执行 // NOTE: 当程序被终止的时候,不能保证执行 cleanup // (终止是 kill 等强制终止,或段错误等行为,程序无法继续执行,资源由操作系统进行回收) pub(crate) fn cleanup() { static CLEANUP: Once = Once::new(); CLEANUP.call_once(|| unsafe { // 刷新 stdout 缓冲区的数据,并禁用缓冲区 crate::io::cleanup(); // SAFETY: 通过 Once 保证,只会执行一次 cleanup sys::cleanup(); }); } 系统资源的初始化和清理在 sys::init 和 sys::cleanup 中 sys::init 在ffi中不保证被调用 sys::init 的源码不是算复杂,主要是保证打开标准输入输出流、初始化栈,感兴趣的可以自行阅读源码 现在我们终于可以进入重点了,rust对main函数的处理逻辑: ...
使用AWS S3 SDK访问阿里云oss
目前业务上使用的是 aws 的 s3 服务,但是想兼容阿里云的 oss。根据oss的文档描述,oss支持使用 aws 的 sdk 进行访问,所以记录一下处理流程 访问AWS S3 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 package main import ( "context" "log" "os" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/credentials" "github.com/aws/aws-sdk-go-v2/service/s3" ) func NewS3Client() *s3.Client { accessKeyID := os.Getenv("ACCESS_KEY_ID") accessKeySecret := os.Getenv("ACCESS_KEY_SECRET") cfg, err := config.LoadDefaultConfig( context.TODO(), config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(accessKeyID, accessKeySecret, "")), config.WithEndpointResolverWithOptions( aws.EndpointResolverWithOptionsFunc(func(_, _ string, _ ...interface{}) (aws.Endpoint, error) { return aws.Endpoint{ PartitionID: "aws-cn", URL: "https://s3.cn-northwest-1.amazonaws.com.cn", SigningRegion: "cn-northwest-1", }, nil }), ), ) if err != nil { log.Fatal(err) } return s3.NewFromConfig(cfg, func(o *s3.Options) { // 此选项可用于调试 // o.ClientLogMode = aws.LogSigning | aws.LogRequest | aws.LogResponseWithBody o.UsePathStyle = true }) } func main() { bucket := os.Getenv("S3_BUCKET") uploadKey := os.Getenv("S3_KEY") file, _ := os.Open("test.txt") client := NewS3Client() _, err := client.PutObject(context.Background(), &s3.PutObjectInput{ Bucket: aws.String(bucket), Key: aws.String(uploadKey), Body: file, }) if err != nil { log.Fatal(err) } } 这是一个简单的 s3 文件上传,通过在 PutObjectInput 中指定Bucket 参数的形式。 ...
通过docker配置MySQL主从服务
目录结构 1 2 3 4 5 6 . ├── master │ └── my.cnf ├── slave │ └── my.cnf └── docker-compose.yml master:主配置 slave:从配置 docker-compose:通过docker-compose进行容器配置和启动 master/my.cnf 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 # For advice on how to change settings please see # http://dev.mysql.com/doc/refman/8.3/en/server-configuration-defaults.html [mysqld] host-cache-size=0 skip-name-resolve datadir=/var/lib/mysql socket=/var/run/mysqld/mysqld.sock secure-file-priv=/var/lib/mysql-files user=mysql pid-file=/var/run/mysqld/mysqld.pid # 自定义部分 log-bin=master-bin binlog-format=row # row 按行重放,statement 重放 sql 语句,mixed 默认基于 statement,一旦发现基于 sql 无法精准重放时,会使用 row,MySQL 默认是基于 statement 的复制 binlog-do-db=test # 开启 binlog 的数据库名,如果有多个数据库,那么可以重复设置 server-id=1 # server-id 不能和任何 主或从 重复 # 自定义部分 [client] socket=/var/run/mysqld/mysqld.sock !includedir /etc/mysql/conf.d/ slave/my.cnf 和master/my.cnf内容基本一致,但是server-id不能重复 ...
Go设计模式——单例模式
介绍 单例模式同时解决了两个问题: 保证一个类只有一个实例,例如控制某些共享资源(如数据库或文件)的访问权限 为该实例提供一个全局访问节点 在Go中单例模式有两种实现,一种是饿汉式,一种是懒汉式。饿汉式简单,可以将问题及早暴露出来,懒汉式虽然支持延迟加载,但是也将可能的问题延迟到了第一次调用的时候,同时为了实现并发安全,也不得不加锁。 饿汉式 代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 // Package singleton package singleton // singleton 饿汉式 var singleton *File type File struct{} func init() { singleton = &File{} } func GetInstance() *File { return singleton } 单元测试 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package tests import ( "testing" "design-pattern/singleton" "github.com/stretchr/testify/assert" ) // TestSingleton 测试单例 func TestInstance(t *testing.T) { assert.Equal(t, singleton.GetInstance(), singleton.GetInstance()) } // BenchmarkSingleton 测试并发访问单例 func BenchmarkInstance(b *testing.B) { b.RunParallel(func(p *testing.PB) { for p.Next() { assert.Equal(b, singleton.GetInstance(), singleton.GetInstance()) } }) } 懒汉式 代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 // Package lazysingleton 懒汉式单例 package lazysingleton import "sync" var ( lazySingleton *File once sync.Once ) type File struct{} func GetLazyInstance() *File { if lazySingleton == nil { // 使用sync.Once来确保单例在并发时只被初始化一次 once.Do(func() { lazySingleton = &File{} }) } return lazySingleton } 单元测试 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package tests import ( "testing" "design-pattern/lazysingleton" "github.com/stretchr/testify/assert" ) // TestLazySingleton 测试懒汉式单例 func TestLazyInstance(t *testing.T) { assert.Equal(t, lazysingleton.GetLazyInstance(), lazysingleton.GetLazyInstance()) } // BenchmarkGetLazyInstance 测试懒汉式单例 func BenchmarkGetLazyInstance(b *testing.B) { b.RunParallel(func(p *testing.PB) { for p.Next() { assert.Equal(b, lazysingleton.GetLazyInstance(), lazysingleton.GetLazyInstance()) } }) } 测试结果 1 2 3 4 5 6 7 8 ❯ go test -bench . goos: darwin goarch: arm64 pkg: design-pattern/tests BenchmarkGetLazyInstance-8 3113553 380.8 ns/op BenchmarkInstance-8 3156261 378.5 ns/op PASS ok design-pattern/tests 3.798s 根据bench结果可以发现加锁(sync.Once内部使用的atomic来进行处理)还是会对性能造成影响的,不过也在可接受范围内。
Go设计模式——开闭原则
介绍 简单的说就是:对扩展开放,对修改关闭。对扩展开放是为了应对需求的变化,对修改关闭就是为了保证已有代码的稳定性,最终是为了让系统更具有弹性,能更好的处理需求。 开闭原则也包含了单一职责原则。 我们以消息队列来进行举例。 坏的 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 // Package main 开闭原则 Open-Closed Principle // 开闭原则包含了:单一职责原则 package main import "fmt" type KafkaQueue struct{} func (k *KafkaQueue) SendMSG(msg string) error { fmt.Println("Kafka send msg success") return nil } type RabbitQueue struct{} func (r *RabbitQueue) SendMSG(msg string) error { fmt.Println("Rabbitmq send msg success") return nil } type Demo struct{} func (d *Demo) SendByKafka(queue KafkaQueue, msg string) error { return queue.SendMSG(msg) } func (d *Demo) SendByRabbit(queue RabbitQueue, msg string) error { return queue.SendMSG(msg) } func main() { } 通过这个例子,我们可以看出来,这段代码违背了我们的对扩展开放,对修改关闭的原则。当我们需要添加一个新的RocketMQ的时候,需要改动Demo的逻辑以及其他设计的业务逻辑,可扩展性可以说是一点也没有。 ...
Go设计模式——单一职责原则
介绍 类的职责应该是单一的,对外只提供一种功能,而引起类变化的原因应该只有一个。简单的说就是每一个类只负责自己的事情,只有单一的功能。 我们现在以银行工作人员举例: 坏的 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 // Package main 单一职责原则 Single-Responsibility Principle package main import "fmt" type Banker struct{} // Save 存钱 func (b *Banker) Save(money uint64) error { fmt.Printf("成功存入: %d\n", money) return nil } // Transfer 转账 func (b *Banker) Transfer(money uint64, to string) error { fmt.Printf("成功向: %s转入: %d\n", to, money) return nil } 单一职责原则要求一个类/接口只有一个职责,而引起类变化的原因只能有一个。 ...