✅ Add stability testing
Some checks are pending
Stability Tests / Stability Tests (1.19, macos-latest) (push) Waiting to run
Stability Tests / Stability Tests (1.19, ubuntu-latest) (push) Waiting to run
Stability Tests / Stability Tests (1.19, windows-latest) (push) Waiting to run
Stability Tests / Stability Tests (1.20, macos-latest) (push) Waiting to run
Stability Tests / Stability Tests (1.20, ubuntu-latest, true) (push) Waiting to run
Stability Tests / Stability Tests (1.20, windows-latest) (push) Waiting to run
Some checks are pending
Stability Tests / Stability Tests (1.19, macos-latest) (push) Waiting to run
Stability Tests / Stability Tests (1.19, ubuntu-latest) (push) Waiting to run
Stability Tests / Stability Tests (1.19, windows-latest) (push) Waiting to run
Stability Tests / Stability Tests (1.20, macos-latest) (push) Waiting to run
Stability Tests / Stability Tests (1.20, ubuntu-latest, true) (push) Waiting to run
Stability Tests / Stability Tests (1.20, windows-latest) (push) Waiting to run
This commit is contained in:
60
.github/workflows/stability-tests.yml
vendored
Normal file
60
.github/workflows/stability-tests.yml
vendored
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
name: Stability Tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ main, master ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ main, master ]
|
||||||
|
schedule:
|
||||||
|
- cron: '0 0 * * 0' # Runs weekly at Sunday midnight
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
stability-tests:
|
||||||
|
name: Stability Tests
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||||
|
go-version: ['1.19', '1.20']
|
||||||
|
include:
|
||||||
|
- os: ubuntu-latest
|
||||||
|
go-version: '1.20'
|
||||||
|
run-long-tests: true
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Install Go ${{ matrix.go-version }}
|
||||||
|
uses: actions/setup-go@v3
|
||||||
|
with:
|
||||||
|
go-version: ${{ matrix.go-version }}
|
||||||
|
|
||||||
|
- name: Display Go Version
|
||||||
|
run: go version
|
||||||
|
|
||||||
|
- name: Install Dependencies
|
||||||
|
run: go mod download
|
||||||
|
|
||||||
|
- name: Run Basic Tests
|
||||||
|
run: go test -v -race
|
||||||
|
|
||||||
|
- name: Run Concurrent Load Test
|
||||||
|
if: ${{ matrix.run-long-tests }}
|
||||||
|
run: go test -v -run TestConcurrentLoad -timeout 30m
|
||||||
|
|
||||||
|
- name: Run Fault Tolerance Test
|
||||||
|
if: ${{ matrix.run-long-tests }}
|
||||||
|
run: go test -v -run TestFaultTolerance -timeout 10m
|
||||||
|
|
||||||
|
- name: Run Resource Constraints Test
|
||||||
|
if: ${{ matrix.run-long-tests }}
|
||||||
|
run: go test -v -run TestResourceConstraints -timeout 20m
|
||||||
|
|
||||||
|
- name: Run Long-term Stability Test
|
||||||
|
if: ${{ matrix.run-long-tests }}
|
||||||
|
run: go test -v -run TestLongRunningStability -timeout 20m
|
||||||
|
|
||||||
|
- name: Run Benchmark Tests
|
||||||
|
if: ${{ matrix.run-long-tests }}
|
||||||
|
run: go test -bench=. -benchmem -timeout 30m
|
2
go.mod
2
go.mod
@@ -1,4 +1,4 @@
|
|||||||
module go-xcipher
|
module github.com/landaiqing/go-xcipher
|
||||||
|
|
||||||
go 1.24.0
|
go 1.24.0
|
||||||
|
|
||||||
|
933
stability_test.go
Normal file
933
stability_test.go
Normal file
@@ -0,0 +1,933 @@
|
|||||||
|
package xcipher
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"crypto/rand"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
mrand "math/rand" // 添加math/rand包并重命名,避免与crypto/rand冲突
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 稳定性测试标准:
|
||||||
|
// 1. 功能正确性: 所有加密/解密操作结果必须与原始数据完全一致
|
||||||
|
// 2. 内存稳定性: 长时间运行后内存使用量不应超过初始值的110%
|
||||||
|
// 3. 错误处理稳定性: 对于错误输入/异常情况应始终有适当的错误处理,不会导致崩溃
|
||||||
|
// 4. 并发稳定性: 在高并发下不应出现数据竞争或死锁
|
||||||
|
// 5. 资源使用: 应合理使用系统资源,不应出现资源泄漏
|
||||||
|
//
|
||||||
|
// 具体稳定性判断标准:
|
||||||
|
|
||||||
|
// TestLongRunningStability 测试加密/解密的长时间运行稳定性
|
||||||
|
//
|
||||||
|
// 稳定标准:
|
||||||
|
// - 正确性: 所有加密/解密操作的结果必须正确,错误率为0
|
||||||
|
// - 内存稳定性: 长时间运行后内存使用不超过初始值的110%,无持续增长趋势
|
||||||
|
// - 运行持续性: 能够连续运行指定时间而不崩溃
|
||||||
|
//
|
||||||
|
// 不稳定表现:
|
||||||
|
// - 出现任何加密/解密结果不匹配的情况
|
||||||
|
// - 内存使用持续增长,超过初始值的110%
|
||||||
|
// - 运行过程中出现未处理的异常或崩溃
|
||||||
|
func TestLongRunningStability(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("跳过长时间运行测试")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试持续时间
|
||||||
|
duration := 5 * time.Minute // 可根据需要调整
|
||||||
|
|
||||||
|
// 创建密钥
|
||||||
|
key, err := generateRandomKey()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("无法生成密钥: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化加密器
|
||||||
|
xcipher := NewXCipher(key)
|
||||||
|
|
||||||
|
// 设置测试数据大小和缓冲区
|
||||||
|
dataSize := 1 * 1024 * 1024 // 1MB
|
||||||
|
dataSizes := []int{
|
||||||
|
64 * 1024, // 64KB
|
||||||
|
256 * 1024, // 256KB
|
||||||
|
1 * 1024 * 1024, // 1MB
|
||||||
|
}
|
||||||
|
|
||||||
|
startTime := time.Now()
|
||||||
|
endTime := startTime.Add(duration)
|
||||||
|
|
||||||
|
// 统计内存使用情况的变量
|
||||||
|
var memStats runtime.MemStats
|
||||||
|
var initialAlloc uint64
|
||||||
|
var maxAlloc uint64
|
||||||
|
var lastAlloc uint64
|
||||||
|
|
||||||
|
// 获取初始内存状态
|
||||||
|
runtime.GC()
|
||||||
|
runtime.ReadMemStats(&memStats)
|
||||||
|
initialAlloc = memStats.Alloc
|
||||||
|
maxAlloc = initialAlloc
|
||||||
|
lastAlloc = initialAlloc
|
||||||
|
|
||||||
|
// 记录计数
|
||||||
|
var operationCount int64 = 0
|
||||||
|
var totalBytesProcessed int64 = 0
|
||||||
|
var errorCount int64 = 0
|
||||||
|
|
||||||
|
t.Logf("开始长时间稳定性测试,持续时间: %v", duration)
|
||||||
|
t.Logf("初始内存分配: %d MB", initialAlloc/1024/1024)
|
||||||
|
|
||||||
|
// 运行直到时间结束
|
||||||
|
for time.Now().Before(endTime) {
|
||||||
|
// 随机选择数据大小
|
||||||
|
dataSize = dataSizes[operationCount%int64(len(dataSizes))]
|
||||||
|
|
||||||
|
// 生成随机数据
|
||||||
|
testData, err := generateRandomData(dataSize)
|
||||||
|
if err != nil {
|
||||||
|
t.Logf("生成随机数据失败: %v", err)
|
||||||
|
errorCount++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加密
|
||||||
|
var encryptedBuf bytes.Buffer
|
||||||
|
err = xcipher.EncryptStream(bytes.NewReader(testData), &encryptedBuf, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Logf("加密失败: %v", err)
|
||||||
|
errorCount++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解密
|
||||||
|
var decryptedBuf bytes.Buffer
|
||||||
|
err = xcipher.DecryptStream(bytes.NewReader(encryptedBuf.Bytes()), &decryptedBuf, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Logf("解密失败: %v", err)
|
||||||
|
errorCount++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证解密结果
|
||||||
|
if !bytes.Equal(testData, decryptedBuf.Bytes()) {
|
||||||
|
t.Logf("加密/解密后数据不匹配,操作计数: %d", operationCount)
|
||||||
|
errorCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
operationCount++
|
||||||
|
totalBytesProcessed += int64(dataSize)
|
||||||
|
|
||||||
|
// 每100次操作检查一次内存使用情况
|
||||||
|
if operationCount%100 == 0 {
|
||||||
|
runtime.GC()
|
||||||
|
runtime.ReadMemStats(&memStats)
|
||||||
|
currentAlloc := memStats.Alloc
|
||||||
|
|
||||||
|
if currentAlloc > maxAlloc {
|
||||||
|
maxAlloc = currentAlloc
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查内存增长趋势
|
||||||
|
memDiff := int64(currentAlloc) - int64(lastAlloc)
|
||||||
|
t.Logf("操作次数: %d, 当前内存: %d MB, 内存变化: %d KB",
|
||||||
|
operationCount, currentAlloc/1024/1024, memDiff/1024)
|
||||||
|
|
||||||
|
lastAlloc = currentAlloc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 统计完整运行信息
|
||||||
|
runtime.GC()
|
||||||
|
runtime.ReadMemStats(&memStats)
|
||||||
|
finalAlloc := memStats.Alloc
|
||||||
|
|
||||||
|
testDuration := time.Since(startTime)
|
||||||
|
t.Logf("长时间稳定性测试完成,持续时间: %v", testDuration)
|
||||||
|
t.Logf("总操作次数: %d, 总处理数据量: %d MB", operationCount, totalBytesProcessed/1024/1024)
|
||||||
|
t.Logf("错误次数: %d (%.2f%%)", errorCount, float64(errorCount)*100/float64(operationCount))
|
||||||
|
t.Logf("最终内存分配: %d MB (初始: %d MB, 最大: %d MB)",
|
||||||
|
finalAlloc/1024/1024, initialAlloc/1024/1024, maxAlloc/1024/1024)
|
||||||
|
|
||||||
|
// 验证稳定性结果
|
||||||
|
if errorCount > 0 {
|
||||||
|
t.Errorf("稳定性测试中发现错误: %d 次错误,共 %d 次操作", errorCount, operationCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 内存稳定性评估(允许最终内存比初始内存增长不超过10%)
|
||||||
|
if finalAlloc > initialAlloc && float64(finalAlloc-initialAlloc)/float64(initialAlloc) > 0.1 {
|
||||||
|
t.Errorf("可能存在内存泄漏: 初始内存 %d MB, 最终内存 %d MB, 增长 %.2f%%",
|
||||||
|
initialAlloc/1024/1024, finalAlloc/1024/1024,
|
||||||
|
float64(finalAlloc-initialAlloc)*100/float64(initialAlloc))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestConcurrentLoad 测试高并发下的加密解密稳定性
|
||||||
|
//
|
||||||
|
// 稳定标准:
|
||||||
|
// - 高并发下错误率不超过0.1%
|
||||||
|
// - 随着并发级别提高,系统吞吐量应保持合理增长,不应出现明显下降
|
||||||
|
// - 所有goroutine能够正常完成工作,不出现死锁
|
||||||
|
// - 资源使用(如内存、CPU)随并发数增加应有合理的扩展性
|
||||||
|
//
|
||||||
|
// 不稳定表现:
|
||||||
|
// - 并发环境下出现超过0.1%的错误率
|
||||||
|
// - 吞吐量随并发增加反而下降(扩展效率低于70%)
|
||||||
|
// - 出现死锁或goroutine泄漏
|
||||||
|
// - 任何数据一致性问题
|
||||||
|
func TestConcurrentLoad(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("跳过高并发负载测试")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试参数
|
||||||
|
concurrencyLevels := []int{4, 8, 16, 32, 64} // 并发级别
|
||||||
|
if runtime.NumCPU() < 8 {
|
||||||
|
// 对于低核心数CPU,减少最大并发
|
||||||
|
concurrencyLevels = []int{4, 8, 16}
|
||||||
|
}
|
||||||
|
|
||||||
|
duration := 1 * time.Minute // 每个并发级别测试时间
|
||||||
|
|
||||||
|
// 创建密钥
|
||||||
|
key, err := generateRandomKey()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("无法生成密钥: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化加密器
|
||||||
|
xcipher := NewXCipher(key)
|
||||||
|
|
||||||
|
// 测试不同数据大小
|
||||||
|
dataSizes := []int{
|
||||||
|
16 * 1024, // 16KB
|
||||||
|
64 * 1024, // 64KB
|
||||||
|
256 * 1024, // 256KB
|
||||||
|
1 * 1024 * 1024, // 1MB
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化随机数生成器
|
||||||
|
randSource := mrand.NewSource(time.Now().UnixNano())
|
||||||
|
randGen := mrand.New(randSource)
|
||||||
|
|
||||||
|
// 记录每个并发级别的性能
|
||||||
|
type result struct {
|
||||||
|
concurrency int
|
||||||
|
opsPerSecond float64
|
||||||
|
bytesPerSecond int64
|
||||||
|
errorRate float64
|
||||||
|
avgResponseTimeMs float64
|
||||||
|
}
|
||||||
|
|
||||||
|
var results []result
|
||||||
|
|
||||||
|
// 测试不同的并发级别
|
||||||
|
for _, concurrency := range concurrencyLevels {
|
||||||
|
t.Logf("测试并发级别: %d", concurrency)
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
var totalOps int64 = 0
|
||||||
|
var totalErrors int64 = 0
|
||||||
|
var totalBytes int64 = 0
|
||||||
|
var totalTimeNs int64 = 0
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), duration)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
// 互斥锁保护共享计数器
|
||||||
|
var mu sync.Mutex
|
||||||
|
|
||||||
|
// 启动工作goroutine
|
||||||
|
for i := 0; i < concurrency; i++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(workerID int) {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
// 每个worker的本地计数
|
||||||
|
localOps := int64(0)
|
||||||
|
localErrors := int64(0)
|
||||||
|
localBytes := int64(0)
|
||||||
|
localTimeNs := int64(0)
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
// 测试时间到,更新全局计数并退出
|
||||||
|
mu.Lock()
|
||||||
|
totalOps += localOps
|
||||||
|
totalErrors += localErrors
|
||||||
|
totalBytes += localBytes
|
||||||
|
totalTimeNs += localTimeNs
|
||||||
|
mu.Unlock()
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
// 随机选择数据大小
|
||||||
|
dataSize := dataSizes[randGen.Intn(len(dataSizes))]
|
||||||
|
|
||||||
|
// 生成随机测试数据
|
||||||
|
start := time.Now()
|
||||||
|
testData, err := generateRandomData(dataSize)
|
||||||
|
if err != nil {
|
||||||
|
localErrors++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行加密
|
||||||
|
var encryptedBuf bytes.Buffer
|
||||||
|
err = xcipher.EncryptStream(bytes.NewReader(testData), &encryptedBuf, nil)
|
||||||
|
if err != nil {
|
||||||
|
localErrors++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行解密
|
||||||
|
var decryptedBuf bytes.Buffer
|
||||||
|
err = xcipher.DecryptStream(bytes.NewReader(encryptedBuf.Bytes()), &decryptedBuf, nil)
|
||||||
|
if err != nil {
|
||||||
|
localErrors++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
if !bytes.Equal(testData, decryptedBuf.Bytes()) {
|
||||||
|
localErrors++
|
||||||
|
}
|
||||||
|
|
||||||
|
elapsedTime := time.Since(start)
|
||||||
|
localOps++
|
||||||
|
localBytes += int64(dataSize)
|
||||||
|
localTimeNs += elapsedTime.Nanoseconds()
|
||||||
|
|
||||||
|
// 每100个操作报告一次进度
|
||||||
|
if localOps%100 == 0 {
|
||||||
|
mu.Lock()
|
||||||
|
t.Logf("Worker %d: 完成 %d 次操作", workerID, localOps)
|
||||||
|
mu.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 等待测试时间结束
|
||||||
|
<-ctx.Done()
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
// 计算性能指标
|
||||||
|
durationSeconds := float64(duration.Seconds())
|
||||||
|
opsPerSecond := float64(totalOps) / durationSeconds
|
||||||
|
bytesPerSecond := int64(float64(totalBytes) / durationSeconds)
|
||||||
|
errorRate := float64(0)
|
||||||
|
if totalOps > 0 {
|
||||||
|
errorRate = float64(totalErrors) * 100 / float64(totalOps)
|
||||||
|
}
|
||||||
|
avgResponseTimeMs := float64(0)
|
||||||
|
if totalOps > 0 {
|
||||||
|
avgResponseTimeMs = float64(totalTimeNs) / float64(totalOps) / float64(1000000)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 记录结果
|
||||||
|
results = append(results, result{
|
||||||
|
concurrency: concurrency,
|
||||||
|
opsPerSecond: opsPerSecond,
|
||||||
|
bytesPerSecond: bytesPerSecond,
|
||||||
|
errorRate: errorRate,
|
||||||
|
avgResponseTimeMs: avgResponseTimeMs,
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Logf("并发级别 %d 结果: %.2f 操作/秒, %.2f MB/秒, 错误率 %.4f%%, 平均响应时间 %.2f 毫秒",
|
||||||
|
concurrency, opsPerSecond, float64(bytesPerSecond)/(1024*1024), errorRate, avgResponseTimeMs)
|
||||||
|
|
||||||
|
// 稳定性验证
|
||||||
|
if errorRate > 0.1 { // 允许0.1%的错误率
|
||||||
|
t.Errorf("并发级别 %d 的错误率过高: %.4f%%", concurrency, errorRate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分析性能扩展性
|
||||||
|
if len(results) > 1 {
|
||||||
|
baseResult := results[0]
|
||||||
|
for i := 1; i < len(results); i++ {
|
||||||
|
scalingFactor := results[i].opsPerSecond / baseResult.opsPerSecond
|
||||||
|
theoreticalScaling := float64(results[i].concurrency) / float64(baseResult.concurrency)
|
||||||
|
scalingEfficiency := scalingFactor / theoreticalScaling * 100
|
||||||
|
|
||||||
|
t.Logf("从 %d 到 %d 的并发扩展性: %.2f%% (理论扩展比: %.2f, 实际扩展比: %.2f)",
|
||||||
|
baseResult.concurrency, results[i].concurrency,
|
||||||
|
scalingEfficiency, theoreticalScaling, scalingFactor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestFaultTolerance 测试各种故障情况下的恢复能力
|
||||||
|
//
|
||||||
|
// 稳定标准:
|
||||||
|
// - 数据篡改: 能够正确检测到任何篡改数据,拒绝解密
|
||||||
|
// - IO故障: 对IO错误有适当处理,不会导致程序崩溃
|
||||||
|
// - 不完整数据: 能够正确处理各种不完整数据,返回明确错误信息
|
||||||
|
// - 随机输入: 对完全随机的输入数据能够安全处理,不会崩溃
|
||||||
|
//
|
||||||
|
// 不稳定表现:
|
||||||
|
// - 未能检测出篡改的数据
|
||||||
|
// - IO错误导致程序崩溃或状态不一致
|
||||||
|
// - 对不完整数据或随机输入没有适当的错误处理
|
||||||
|
// - 出现未预期的异常或崩溃
|
||||||
|
func TestFaultTolerance(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("跳过容错性测试")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建密钥
|
||||||
|
key, err := generateRandomKey()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("无法生成密钥: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化加密器
|
||||||
|
xcipher := NewXCipher(key)
|
||||||
|
|
||||||
|
// 不同的测试场景
|
||||||
|
t.Run("数据篡改", func(t *testing.T) {
|
||||||
|
// 创建测试数据
|
||||||
|
testData, err := generateRandomData(64 * 1024)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("无法生成测试数据: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行加密
|
||||||
|
var encBuf bytes.Buffer
|
||||||
|
err = xcipher.EncryptStream(bytes.NewReader(testData), &encBuf, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("加密失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
encData := encBuf.Bytes()
|
||||||
|
|
||||||
|
// 对加密数据进行篡改
|
||||||
|
tamperedData := make([]byte, len(encData))
|
||||||
|
copy(tamperedData, encData)
|
||||||
|
|
||||||
|
// 篡改不同位置的数据
|
||||||
|
tamperPositions := []int{
|
||||||
|
0, // 篡改开头
|
||||||
|
len(encData) / 2, // 篡改中间
|
||||||
|
len(encData) - 1, // 篡改末尾
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, pos := range tamperPositions {
|
||||||
|
if pos < len(tamperedData) {
|
||||||
|
tamperedData[pos] ^= 0xFF // 翻转位
|
||||||
|
|
||||||
|
// 尝试解密篡改后的数据
|
||||||
|
var decBuf bytes.Buffer
|
||||||
|
err = xcipher.DecryptStream(bytes.NewReader(tamperedData), &decBuf, nil)
|
||||||
|
|
||||||
|
// 应该返回认证错误
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("篡改位置 %d: 期望解密失败,但解密成功", pos)
|
||||||
|
} else {
|
||||||
|
t.Logf("篡改位置 %d: 正确检测到数据篡改: %v", pos, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 恢复篡改
|
||||||
|
tamperedData[pos] = encData[pos]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("IO故障恢复", func(t *testing.T) {
|
||||||
|
// 创建测试数据
|
||||||
|
testData, err := generateRandomData(256 * 1024)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("无法生成测试数据: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用故障注入读取器和写入器
|
||||||
|
faultReader := &faultInjectionReader{
|
||||||
|
data: testData,
|
||||||
|
failureProb: 0.01, // 1%的概率失败
|
||||||
|
maxFailCount: 5, // 最多失败5次
|
||||||
|
}
|
||||||
|
|
||||||
|
faultWriter := &faultInjectionWriter{
|
||||||
|
buffer: &bytes.Buffer{},
|
||||||
|
failureProb: 0.01, // 1%的概率失败
|
||||||
|
maxFailCount: 5, // 最多失败5次
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置取消选项以便在一定时间后取消操作
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
options := DefaultStreamOptions()
|
||||||
|
options.CancelChan = ctx.Done()
|
||||||
|
options.CollectStats = true
|
||||||
|
|
||||||
|
// 尝试加密
|
||||||
|
stats, err := xcipher.EncryptStreamWithOptions(faultReader, faultWriter, options)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
// 如果因IO故障失败,这是预期的
|
||||||
|
t.Logf("加密遇到故障: %v", err)
|
||||||
|
t.Logf("注入的读取故障: %d, 写入故障: %d",
|
||||||
|
faultReader.failCount, faultWriter.failCount)
|
||||||
|
|
||||||
|
// 验证是否是预期的故障类型
|
||||||
|
if errors.Is(err, ErrReadFailed) || errors.Is(err, ErrWriteFailed) ||
|
||||||
|
errors.Is(err, ErrOperationCancelled) {
|
||||||
|
t.Logf("成功识别故障类型: %v", err)
|
||||||
|
} else {
|
||||||
|
t.Errorf("意外的错误类型: %v", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 如果完成,检查IO故障注入的次数
|
||||||
|
t.Logf("成功完成加密,尽管有故障注入")
|
||||||
|
t.Logf("注入的读取故障: %d, 写入故障: %d",
|
||||||
|
faultReader.failCount, faultWriter.failCount)
|
||||||
|
t.Logf("处理统计: 字节=%d, 区块=%d, 吞吐量=%.2f MB/s",
|
||||||
|
stats.BytesProcessed, stats.BlocksProcessed, stats.Throughput)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("不完整数据", func(t *testing.T) {
|
||||||
|
// 创建测试数据
|
||||||
|
testData, err := generateRandomData(64 * 1024)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("无法生成测试数据: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行加密
|
||||||
|
var encBuf bytes.Buffer
|
||||||
|
err = xcipher.EncryptStream(bytes.NewReader(testData), &encBuf, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("加密失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
encData := encBuf.Bytes()
|
||||||
|
|
||||||
|
// 尝试解密不完整的数据
|
||||||
|
truncateSizes := []int{
|
||||||
|
0, // 空数据
|
||||||
|
nonceSize - 1, // 不完整nonce
|
||||||
|
nonceSize, // 只有nonce,没有加密数据
|
||||||
|
nonceSize + 10, // nonce+部分数据
|
||||||
|
len(encData) / 2, // 数据一半
|
||||||
|
len(encData) - 10, // 接近完整数据
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, size := range truncateSizes {
|
||||||
|
if size > len(encData) {
|
||||||
|
size = len(encData)
|
||||||
|
}
|
||||||
|
|
||||||
|
truncatedData := encData[:size]
|
||||||
|
var decBuf bytes.Buffer
|
||||||
|
|
||||||
|
err = xcipher.DecryptStream(bytes.NewReader(truncatedData), &decBuf, nil)
|
||||||
|
if err == nil && size < len(encData) {
|
||||||
|
t.Errorf("截断大小 %d: 期望解密失败,但解密成功", size)
|
||||||
|
} else {
|
||||||
|
t.Logf("截断大小 %d: 正确处理: %v", size, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("随机输入", func(t *testing.T) {
|
||||||
|
// 测试完全随机的输入数据
|
||||||
|
sizes := []int{1, 16, 64, 256, 1024, 4096, 16384}
|
||||||
|
|
||||||
|
for _, size := range sizes {
|
||||||
|
randomData, err := generateRandomData(size)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("无法生成随机数据: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var decBuf bytes.Buffer
|
||||||
|
err = xcipher.DecryptStream(bytes.NewReader(randomData), &decBuf, nil)
|
||||||
|
|
||||||
|
// 应该失败,不应该导致panic
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("随机输入大小 %d: 期望解密失败,但解密成功", size)
|
||||||
|
} else {
|
||||||
|
t.Logf("随机输入大小 %d: 正确处理随机输入: %v", size, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 故障注入读取器 - 模拟IO读取错误
|
||||||
|
type faultInjectionReader struct {
|
||||||
|
data []byte
|
||||||
|
pos int
|
||||||
|
failureProb float64
|
||||||
|
failCount int
|
||||||
|
maxFailCount int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *faultInjectionReader) Read(p []byte) (n int, err error) {
|
||||||
|
// 检查是否已结束
|
||||||
|
if r.pos >= len(r.data) {
|
||||||
|
return 0, io.EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果未达到最大失败次数且随机数小于失败概率,则注入错误
|
||||||
|
if r.failCount < r.maxFailCount && mrand.Float64() < r.failureProb {
|
||||||
|
r.failCount++
|
||||||
|
return 0, fmt.Errorf("模拟读取故障 #%d", r.failCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 正常读取
|
||||||
|
n = copy(p, r.data[r.pos:])
|
||||||
|
r.pos += n
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 故障注入写入器 - 模拟IO写入错误
|
||||||
|
type faultInjectionWriter struct {
|
||||||
|
buffer *bytes.Buffer
|
||||||
|
failureProb float64
|
||||||
|
failCount int
|
||||||
|
maxFailCount int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *faultInjectionWriter) Write(p []byte) (n int, err error) {
|
||||||
|
// 如果未达到最大失败次数且随机数小于失败概率,则注入错误
|
||||||
|
if w.failCount < w.maxFailCount && mrand.Float64() < w.failureProb {
|
||||||
|
w.failCount++
|
||||||
|
return 0, fmt.Errorf("模拟写入故障 #%d", w.failCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 正常写入
|
||||||
|
return w.buffer.Write(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestResourceConstraints 测试在资源限制条件下的稳定性
|
||||||
|
//
|
||||||
|
// 稳定标准:
|
||||||
|
// - 极限缓冲区: 在极小缓冲区下能自动调整到有效值并正常工作
|
||||||
|
// - 极大数据量: 处理大数据量时不会导致内存溢出,能高效处理
|
||||||
|
// - 内存限制: 在内存受限情况下能够优雅降级,合理分配资源
|
||||||
|
// - 资源适应性: 能根据系统条件自动调整资源使用,保持正确性
|
||||||
|
//
|
||||||
|
// 不稳定表现:
|
||||||
|
// - 极限条件下出现未处理的错误或崩溃
|
||||||
|
// - 内存使用失控,导致OOM或系统资源耗尽
|
||||||
|
// - 无法适应资源限制,性能急剧下降
|
||||||
|
// - 数据处理不正确或丢失数据
|
||||||
|
func TestResourceConstraints(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("跳过资源限制测试")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建密钥
|
||||||
|
key, err := generateRandomKey()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("无法生成密钥: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化加密器
|
||||||
|
xcipher := NewXCipher(key)
|
||||||
|
|
||||||
|
// 测试极小缓冲区
|
||||||
|
t.Run("极小缓冲区", func(t *testing.T) {
|
||||||
|
// 创建测试数据
|
||||||
|
testData, err := generateRandomData(256 * 1024) // 256KB数据
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("无法生成测试数据: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用极小的缓冲区尺寸
|
||||||
|
options := DefaultStreamOptions()
|
||||||
|
options.BufferSize = 16 // 故意设置极小值,应该自动调整为最小有效值
|
||||||
|
options.CollectStats = true
|
||||||
|
|
||||||
|
var encBuf bytes.Buffer
|
||||||
|
stats, err := xcipher.EncryptStreamWithOptions(bytes.NewReader(testData), &encBuf, options)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("使用极小缓冲区加密失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证缓冲区是否被自动调整到最小值
|
||||||
|
t.Logf("请求的缓冲区大小: %d, 实际使用: %d", options.BufferSize, stats.BufferSize)
|
||||||
|
if stats.BufferSize < minBufferSize {
|
||||||
|
t.Errorf("缓冲区大小没有被正确调整: %d < %d", stats.BufferSize, minBufferSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解密
|
||||||
|
var decBuf bytes.Buffer
|
||||||
|
_, err = xcipher.DecryptStreamWithOptions(bytes.NewReader(encBuf.Bytes()), &decBuf, options)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("使用极小缓冲区解密失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证数据完整性
|
||||||
|
if !bytes.Equal(testData, decBuf.Bytes()) {
|
||||||
|
t.Fatal("使用极小缓冲区加密/解密后数据不匹配")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 测试极大数据量
|
||||||
|
t.Run("极大数据量", func(t *testing.T) {
|
||||||
|
// 使用流式生成器模拟大数据,避免一次性分配太多内存
|
||||||
|
dataSize := 200 * 1024 * 1024 // 200MB
|
||||||
|
if testing.Short() {
|
||||||
|
dataSize = 20 * 1024 * 1024 // 短测试模式下降为20MB
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建数据生成器
|
||||||
|
dataGenerator := &largeDataGenerator{
|
||||||
|
size: dataSize,
|
||||||
|
chunkSize: 64 * 1024, // 64KB块
|
||||||
|
randGen: mrand.New(mrand.NewSource(time.Now().UnixNano())), // 初始化随机生成器
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用可调节的缓冲区大小
|
||||||
|
bufferSizes := []int{
|
||||||
|
32 * 1024, // 32KB,较小
|
||||||
|
128 * 1024, // 128KB,适中
|
||||||
|
512 * 1024, // 512KB,较大
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, bufSize := range bufferSizes {
|
||||||
|
t.Run(fmt.Sprintf("缓冲区%dKB", bufSize/1024), func(t *testing.T) {
|
||||||
|
// 重置数据生成器位置
|
||||||
|
dataGenerator.Reset()
|
||||||
|
|
||||||
|
// 创建处理选项
|
||||||
|
options := DefaultStreamOptions()
|
||||||
|
options.BufferSize = bufSize
|
||||||
|
options.UseParallel = true // 使用并行模式
|
||||||
|
options.CollectStats = true
|
||||||
|
|
||||||
|
// 创建输出缓冲区
|
||||||
|
outBuf := &limitedBuffer{
|
||||||
|
maxSize: 300 * 1024 * 1024, // 300MB 限制
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尝试加密
|
||||||
|
startTime := time.Now()
|
||||||
|
stats, err := xcipher.EncryptStreamWithOptions(dataGenerator, outBuf, options)
|
||||||
|
duration := time.Since(startTime)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("大数据量加密失败 (size=%dMB, buffer=%dKB): %v",
|
||||||
|
dataSize/(1024*1024), bufSize/1024, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("大数据处理成功 (size=%dMB, buffer=%dKB)",
|
||||||
|
dataSize/(1024*1024), bufSize/1024)
|
||||||
|
t.Logf("处理时间: %v, 吞吐量: %.2f MB/s, 使用并行: %v, 工作线程: %d",
|
||||||
|
duration, stats.Throughput, stats.ParallelProcessing, stats.WorkerCount)
|
||||||
|
|
||||||
|
// 验证加密数据量
|
||||||
|
if outBuf.size < int64(dataSize) {
|
||||||
|
t.Errorf("加密数据大小不正确: 期望>=%d, 实际=%d", dataSize, outBuf.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 限制较小的解密测试量,避免OOM
|
||||||
|
decryptTestSize := 1 * 1024 * 1024 // 1MB
|
||||||
|
if outBuf.size < int64(decryptTestSize) {
|
||||||
|
decryptTestSize = int(outBuf.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解密部分数据用于验证
|
||||||
|
dataSlice := outBuf.Bytes()[:decryptTestSize]
|
||||||
|
|
||||||
|
// 重置生成器以供验证
|
||||||
|
dataGenerator.Reset()
|
||||||
|
originalSlice := make([]byte, decryptTestSize)
|
||||||
|
_, err = io.ReadFull(dataGenerator, originalSlice)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("无法从生成器读取验证数据: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解密
|
||||||
|
var decBuf bytes.Buffer
|
||||||
|
_, err = xcipher.DecryptStreamWithOptions(
|
||||||
|
bytes.NewReader(dataSlice), &decBuf, options)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("部分解密失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证解密的部分
|
||||||
|
if !bytes.Equal(originalSlice, decBuf.Bytes()) {
|
||||||
|
t.Error("解密后的数据片段与原始数据不匹配")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 测试内存限制
|
||||||
|
t.Run("内存限制", func(t *testing.T) {
|
||||||
|
// 创建测试数据
|
||||||
|
dataSize := 32 * 1024 * 1024 // 32MB数据
|
||||||
|
|
||||||
|
// 使用可控数据生成器
|
||||||
|
dataGenerator := &largeDataGenerator{
|
||||||
|
size: dataSize,
|
||||||
|
chunkSize: 64 * 1024, // 64KB块
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建选项,强制使用小内存
|
||||||
|
options := DefaultStreamOptions()
|
||||||
|
options.BufferSize = 16 * 1024 // 16KB,较小的缓冲区
|
||||||
|
options.MaxWorkers = 2 // 限制工作线程
|
||||||
|
options.CollectStats = true
|
||||||
|
|
||||||
|
// 创建内存有限的输出
|
||||||
|
outBuf := &limitedBuffer{
|
||||||
|
maxSize: 40 * 1024 * 1024, // 40MB限制
|
||||||
|
}
|
||||||
|
|
||||||
|
// 运行加密
|
||||||
|
stats, err := xcipher.EncryptStreamWithOptions(dataGenerator, outBuf, options)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("内存限制测试加密失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("内存限制测试结果:")
|
||||||
|
t.Logf("- 处理数据量: %d MB", stats.BytesProcessed/(1024*1024))
|
||||||
|
t.Logf("- 区块数: %d", stats.BlocksProcessed)
|
||||||
|
t.Logf("- 平均区块大小: %.2f KB", stats.AvgBlockSize/1024)
|
||||||
|
t.Logf("- 缓冲区大小: %d KB", stats.BufferSize/1024)
|
||||||
|
t.Logf("- 工作线程数: %d", stats.WorkerCount)
|
||||||
|
t.Logf("- 处理时间: %v", stats.Duration())
|
||||||
|
t.Logf("- 吞吐量: %.2f MB/s", stats.Throughput)
|
||||||
|
|
||||||
|
// 使用Go内置的内存统计
|
||||||
|
var memStats runtime.MemStats
|
||||||
|
runtime.GC()
|
||||||
|
runtime.ReadMemStats(&memStats)
|
||||||
|
|
||||||
|
t.Logf("处理后内存统计:")
|
||||||
|
t.Logf("- 堆内存分配: %d MB", memStats.Alloc/(1024*1024))
|
||||||
|
t.Logf("- 系统内存: %d MB", memStats.Sys/(1024*1024))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 大数据生成器 - 可以生成任意大小的数据流,而不会一次性占用内存
|
||||||
|
type largeDataGenerator struct {
|
||||||
|
size int // 总数据大小
|
||||||
|
chunkSize int // 块大小
|
||||||
|
generated int // 已生成的数据量
|
||||||
|
chunk []byte // 当前块
|
||||||
|
randGen *mrand.Rand // 随机数生成器
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *largeDataGenerator) Read(p []byte) (n int, err error) {
|
||||||
|
if g.generated >= g.size {
|
||||||
|
return 0, io.EOF // 已生成全部数据
|
||||||
|
}
|
||||||
|
|
||||||
|
// 惰性初始化块和随机生成器
|
||||||
|
if g.chunk == nil {
|
||||||
|
g.chunk = make([]byte, g.chunkSize)
|
||||||
|
// 如果没有随机生成器,则创建一个
|
||||||
|
if g.randGen == nil {
|
||||||
|
g.randGen = mrand.New(mrand.NewSource(time.Now().UnixNano()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用crypto/rand生成随机数据,这是为了安全随机性
|
||||||
|
_, err := rand.Read(g.chunk)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算待拷贝的数据量
|
||||||
|
remaining := g.size - g.generated
|
||||||
|
toCopy := len(p)
|
||||||
|
if toCopy > remaining {
|
||||||
|
toCopy = remaining
|
||||||
|
}
|
||||||
|
|
||||||
|
// 拷贝数据到目标缓冲区
|
||||||
|
copied := 0
|
||||||
|
for copied < toCopy {
|
||||||
|
// 确定要从块中拷贝多少
|
||||||
|
chunkOffset := g.generated % g.chunkSize
|
||||||
|
chunkRemaining := g.chunkSize - chunkOffset
|
||||||
|
copyNow := toCopy - copied
|
||||||
|
if copyNow > chunkRemaining {
|
||||||
|
copyNow = chunkRemaining
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行拷贝
|
||||||
|
copy(p[copied:copied+copyNow], g.chunk[chunkOffset:chunkOffset+copyNow])
|
||||||
|
|
||||||
|
copied += copyNow
|
||||||
|
g.generated += copyNow
|
||||||
|
|
||||||
|
// 如果已到达块末尾,可以选择重新生成随机数据
|
||||||
|
// 但为了性能,我们重用相同的块
|
||||||
|
}
|
||||||
|
|
||||||
|
return copied, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *largeDataGenerator) Reset() {
|
||||||
|
g.generated = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// 有限大小的缓冲区 - 模拟内存限制
|
||||||
|
type limitedBuffer struct {
|
||||||
|
data []byte
|
||||||
|
size int64
|
||||||
|
maxSize int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *limitedBuffer) Write(p []byte) (n int, err error) {
|
||||||
|
// 检查是否达到容量上限
|
||||||
|
if b.size+int64(len(p)) > b.maxSize {
|
||||||
|
// 只接受能容纳的部分
|
||||||
|
n = int(b.maxSize - b.size)
|
||||||
|
if n <= 0 {
|
||||||
|
return 0, fmt.Errorf("超出缓冲区最大容量 %d bytes", b.maxSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保有足够空间
|
||||||
|
newSize := b.size + int64(n)
|
||||||
|
if int64(cap(b.data)) < newSize {
|
||||||
|
// 增加容量
|
||||||
|
newData := make([]byte, newSize)
|
||||||
|
copy(newData, b.data)
|
||||||
|
b.data = newData
|
||||||
|
}
|
||||||
|
|
||||||
|
// 追加数据
|
||||||
|
if int64(len(b.data)) < newSize {
|
||||||
|
b.data = b.data[:newSize]
|
||||||
|
}
|
||||||
|
copy(b.data[b.size:], p[:n])
|
||||||
|
b.size = newSize
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 正常情况,接受全部数据
|
||||||
|
newSize := b.size + int64(len(p))
|
||||||
|
|
||||||
|
// 确保有足够空间
|
||||||
|
if int64(cap(b.data)) < newSize {
|
||||||
|
// 增加容量
|
||||||
|
newData := make([]byte, newSize)
|
||||||
|
copy(newData, b.data)
|
||||||
|
b.data = newData
|
||||||
|
}
|
||||||
|
|
||||||
|
// 追加数据
|
||||||
|
if int64(len(b.data)) < newSize {
|
||||||
|
b.data = b.data[:newSize]
|
||||||
|
}
|
||||||
|
copy(b.data[b.size:], p)
|
||||||
|
b.size = newSize
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *limitedBuffer) Bytes() []byte {
|
||||||
|
return b.data
|
||||||
|
}
|
Reference in New Issue
Block a user