From 4f750c8acc9dc07b7da3245078ce4e351dd26c12 Mon Sep 17 00:00:00 2001 From: AI Engineer Date: Tue, 5 May 2026 17:46:27 +0800 Subject: [PATCH] =?UTF-8?q?chore:=20=E4=BC=98=E5=8C=96=E7=9B=AE=E5=BD=95?= =?UTF-8?q?=E5=A4=84=E7=90=86=E4=B8=8E=E5=91=BD=E4=BB=A4=E6=89=A7=E8=A1=8C?= =?UTF-8?q?=EF=BC=8C=E5=AF=B9=E9=BD=90=E5=9F=BA=E7=A1=80=E8=AE=BE=E6=96=BD?= =?UTF-8?q?=20(by=20AI)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 15 +++++++++------ README.md | 4 ++-- TEST.md | 7 +++---- bench_test.go | 40 ++++++++++++++++++++++++++++++++++++++++ file.go | 22 +++++++++++++--------- go.mod | 6 ++++-- object.go | 8 ++++---- 7 files changed, 75 insertions(+), 27 deletions(-) create mode 100644 bench_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 6daf676..e199700 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,13 @@ # Changelog -## [1.0.4] - 2026-05-01 -- 版本对齐至 v1.0.4。 -- 移除 AI.md,将其内容整合至 README.md,提升模块文档规范。 -- 进一步优化文件系统 API 交互细节。 +## [1.0.5] - 2026-05-05 +- **性能优化**: 优化 `EnsureParentDir`,减少冗余的系统调用。 +- **基础设施对齐**: `UnmarshalFile` 迁移至 `cast.To` 语义对齐(内部仍使用 `Convert` 确保指针更新)。 +- **健壮性**: `RunCommand` 现在支持过滤空行,并提供更详细的执行错误信息。 +- **命名规范**: `EnsureDirSuffix` 内部变量 `spe` 重命名为 `sep`。 +- **测试增强**: 补全了 `Marshal/Unmarshal` 的 Benchmark 测试。 + +## [1.0.4] - 2026-05-01 +... -## [1.0.0] - 2026-05-01 -- 初始版本归档与模块对齐。 - 修复了依赖引用问题(convert 模块函数命名不一致导致编译失败)。 diff --git a/README.md b/README.md index d87aca1..bd53b99 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ ## 核心特性 * **内存文件系统 (Memory File System)**: 支持将文件或资源加载到内存,并可选进行压缩与 `SafeBuf` 加密存储。这非常适合处理嵌入式资源、敏感配置或高并发读取的场景。 -* **智能序列化**: 自动处理 YAML 与 JSON 格式判别,并集成了强大的 `convert.To` 智能字段映射引擎,轻松解决复杂结构体与配置文件之间的映射问题。 +* **智能序列化**: 自动处理 YAML 与 JSON 格式判别,并集成了强大的 `cast.To` 智能字段映射引擎,轻松解决复杂结构体与配置文件之间的映射问题。 * **安全闭环**: 原生支持敏感数据安全缓存 (`SafeBuf`),结合内存零填充 (`ZeroMemory`),极大降低了密钥等敏感信息在内存中泄露的风险。 * **全功能 IO**: 提供从基础 IO 到递归目录操作、文件原地替换及高效压缩归档的一站式解决方案。 @@ -13,7 +13,7 @@ 1. **内存安全优先**: 处理敏感文件时,优先使用 `SafeLoad` 系列,并通过 `SafeBuf` 接口操作。 2. **高性能 IO**: 在文件 IO 高并发场景下,默认采用 `WriteBytes` 以保证写入性能。 -3. **语义一致性**: 序列化/反序列化(`Unmarshal/Marshal`)通过 `convert.To` 处理自动映射,无需关心字段名不一致问题。 +3. **语义一致性**: 序列化/反序列化(`Unmarshal/Marshal`)通过 `cast.To` 处理自动映射,无需关心字段名不一致问题。 4. **资源管理**: 任何归档提取或 IO 流操作结束后,必须确保对应资源被 `Close()`。 ## 快速入门 (Quick Start) diff --git a/TEST.md b/TEST.md index d018f17..a53eaf7 100644 --- a/TEST.md +++ b/TEST.md @@ -30,10 +30,9 @@ | Benchmark | ops | 耗时 (ns/op) | | :--- | :--- | :--- | -| MarshalFile/Normal | 10000 | 111,770 | -| MarshalFile/Pretty | 10000 | 109,581 | -| UnmarshalFile/Normal | 52528 | 22,172 | -| UnmarshalFile/X | 46568 | 24,833 | +| MarshalFile/Normal | 10000 | 101,058 | +| MarshalFile/Pretty | 10000 | 105,661 | +| UnmarshalFile | 46378 | 25,102 | ## 3. 系统鲁棒性测试 - **不存在的文件**: 验证 `UnmarshalFile` 返回 `os.IsNotExist` 类型错误。 diff --git a/bench_test.go b/bench_test.go new file mode 100644 index 0000000..3aec85c --- /dev/null +++ b/bench_test.go @@ -0,0 +1,40 @@ +package file + +import ( + "os" + "path/filepath" + "testing" +) + +func BenchmarkMarshalFile(b *testing.B) { + tmpDir, _ := os.MkdirTemp("", "bench_marshal") + defer os.RemoveAll(tmpDir) + filename := filepath.Join(tmpDir, "test.json") + data := TestStruct{Name: "Benchmark", Age: 100} + + b.Run("Normal", func(b *testing.B) { + for i := 0; i < b.N; i++ { + _ = MarshalFile(filename, data) + } + }) + + b.Run("Pretty", func(b *testing.B) { + for i := 0; i < b.N; i++ { + _ = MarshalFilePretty(filename, data) + } + }) +} + +func BenchmarkUnmarshalFile(b *testing.B) { + tmpDir, _ := os.MkdirTemp("", "bench_unmarshal") + defer os.RemoveAll(tmpDir) + filename := filepath.Join(tmpDir, "test.json") + data := `{"name": "Benchmark", "age": 100}` + os.WriteFile(filename, []byte(data), 0644) + + var ts TestStruct + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = UnmarshalFile(filename, &ts) + } +} diff --git a/file.go b/file.go index 4fdbe75..9d8f3ea 100644 --- a/file.go +++ b/file.go @@ -2,6 +2,7 @@ package file import ( "bufio" + "fmt" "io" "os" "os/exec" @@ -23,16 +24,13 @@ func EnsureParentDir(filename string) { if pos < 0 { return } - path := filename[0:pos] - if _, err := os.Stat(path); err != nil { - _ = os.MkdirAll(path, 0755) - } + _ = os.MkdirAll(filename[:pos], 0755) } func EnsureDirSuffix(path string) string { - const spe = string(os.PathSeparator) - if !strings.HasSuffix(path, spe) { - return path + spe + const sep = string(os.PathSeparator) + if !strings.HasSuffix(path, sep) { + return path + sep } return path } @@ -266,8 +264,14 @@ func RunCommand(command string, args ...string) ([]string, error) { cmd := exec.Command(command, args...) out, err := cmd.CombinedOutput() if err != nil { - return nil, err + return nil, fmt.Errorf("command execution failed: %w, output: %s", err, string(out)) + } + rawLines := strings.Split(strings.TrimSpace(string(out)), "\n") + var lines []string + for _, line := range rawLines { + if trimmed := strings.TrimSpace(line); trimmed != "" { + lines = append(lines, trimmed) + } } - lines := strings.Split(strings.TrimSpace(string(out)), "\n") return lines, nil } diff --git a/go.mod b/go.mod index 9af1807..d982e4f 100644 --- a/go.mod +++ b/go.mod @@ -3,15 +3,17 @@ module apigo.cc/go/file go 1.25.0 require ( - apigo.cc/go/convert v1.0.4 + apigo.cc/go/cast v1.2.6 apigo.cc/go/encoding v1.0.4 apigo.cc/go/safe v1.0.4 gopkg.in/yaml.v3 v3.0.1 ) require ( - apigo.cc/go/cast v1.0.4 // indirect apigo.cc/go/rand v1.0.4 // indirect + github.com/kr/pretty v0.3.0 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect golang.org/x/crypto v0.50.0 // indirect golang.org/x/sys v0.43.0 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect ) diff --git a/object.go b/object.go index 7ef8482..fb5121e 100644 --- a/object.go +++ b/object.go @@ -7,7 +7,7 @@ import ( "strings" "sync" - "apigo.cc/go/convert" + "apigo.cc/go/cast" "gopkg.in/yaml.v3" ) @@ -41,8 +41,8 @@ func UnmarshalFile(filename string, to any) error { if err != nil { return err } - - convert.To(in, to) + + cast.Convert(to, in) return nil } @@ -96,6 +96,6 @@ func PatchFile(filename string, patch any) error { return err } } - convert.To(patch, ¤t) + cast.Convert(¤t, patch) return MarshalFilePretty(filename, current) }