KVStreamer 1.7.3
dotnet add package KVStreamer --version 1.7.3
NuGet\Install-Package KVStreamer -Version 1.7.3
<PackageReference Include="KVStreamer" Version="1.7.3" />
<PackageVersion Include="KVStreamer" Version="1.7.3" />
<PackageReference Include="KVStreamer" />
paket add KVStreamer --version 1.7.3
#r "nuget: KVStreamer, 1.7.3"
#:package KVStreamer@1.7.3
#addin nuget:?package=KVStreamer&version=1.7.3
#tool nuget:?package=KVStreamer&version=1.7.3
KVStreamer
中文 | English
A high-performance C# library for Unity that provides streaming key-value pair reading, supports generating compact binary format from CSV files, and features an intelligent cache system with time control.
📦 Installation
NuGet Package
dotnet add package KVStreamer
Or via Package Manager:
Install-Package KVStreamer
Or visit: https://www.nuget.org/packages/KVStreamer/
Unity Installation
- Download the latest release from NuGet
- Extract the .nupkg file (rename to .zip)
- Copy
KVStreamer.dllfromlib/netstandard2.0/to your Unity project'sPluginsfolder
✨ Features
- 📝 CSV to Binary Conversion: Generate optimized binary files from CSV files (ID column as key, Text column as value)
- 🗜️ GZip Compression: Built-in GZip compression support, reduces file size by 60-70% (default enabled)
- 🗺️ Map Header Indexing: Binary files include map headers for fast key-value lookup
- 🚀 Streaming Read: Read using MemoryStream, supports byte[] input, perfect for Unity resource system
- 💾 Smart Caching: Cache system with expiration time, automatically cleans up expired data
- 🎯 Memory Optimized: On-demand value reading, minimizes memory footprint
- 🔒 Thread Safe: File read operations protected with locks
- ⚡ Excellent Performance: Low GC pressure, suitable for mobile platforms and large datasets
- 🔄 Backward Compatible: Automatically detects and loads both compressed and uncompressed formats
- 📖 Dictionary Interface: Implements IDictionary, IReadOnlyDictionary and related interfaces for full compatibility
📦 Project Structure
KVStreamer/
├── KVStreamer.cs # Main class, provides all core APIs
├── ValueCache.cs # Value cache system
├── Example/
│ ├── example_data.csv # Sample CSV data file
│ └── Program.cs # Example usage code
└── README.md
🔧 Binary File Format
The generated .bytes file format is as follows:
[Compression Flag (1 byte)] # 0xC0 = Compressed, 0x00 = Uncompressed
[Compressed/Uncompressed Data]
├── [Map Header Size (4 bytes)]
├── [Map Header Data]
│ ├── [Key1 Length (4 bytes)][Key1 String][Value1 Offset (8 bytes)]
│ ├── [Key2 Length (4 bytes)][Key2 String][Value2 Offset (8 bytes)]
│ └── ...
└── [Value Data]
├── [Value1 Length (4 bytes)][Value1 String]
├── [Value2 Length (4 bytes)][Value2 String]
└── ...
🚀 Quick Start
1. Prepare CSV File
Create a CSV file that must contain ID and Text columns:
ID,Text,Description
item_001,This is the first item,Item description 1
item_002,This is the second item,Item description 2
npc_001,Village chief dialogue text,NPC dialogue
2. Generate Binary File from CSV
using FSTGame;
// Static method - no need to create instance
KVStreamer.CreateBinaryFromCSV("data.csv", "data.bytes");
// Or generate uncompressed file
KVStreamer.CreateBinaryFromCSV("data.csv", "data.bytes", compress: false);
3. Load and Read Data
using FSTGame;
using (KVStreamer streamer = new KVStreamer(cacheDuration: 300f)) // 300 seconds cache
{
// Method 1: Load from file path
streamer.LoadBinaryFile("data.bytes");
// Method 2: Load from byte[] (Recommended for Unity)
byte[] data = File.ReadAllBytes("data.bytes");
streamer.LoadBinaryData(data);
// Get value by key - multiple ways
string text1 = streamer.GetValue("item_001");
string text2 = streamer["item_001"]; // Indexer, throws exception if not found
// TryGetValue pattern (like Dictionary)
if (streamer.TryGetValue("item_001", out string text3))
{
Console.WriteLine(text3);
}
// Use as Dictionary (implements IDictionary<string, string>)
IDictionary<string, string> dict = streamer;
// Use as IReadOnlyDictionary
IReadOnlyDictionary<string, string> readOnlyDict = streamer;
// Enumerate all key-value pairs
foreach (KeyValuePair<string, string> kvp in streamer)
{
Console.WriteLine($"{kvp.Key}: {kvp.Value}");
}
// Access all keys
foreach (string key in streamer.Keys)
{
Console.WriteLine($"{key}: {streamer[key]}");
}
}
📚 API Documentation
KVStreamer Main Class
Implements:
IDictionary<string, string>IReadOnlyDictionary<string, string>ICollection<KeyValuePair<string, string>>IReadOnlyCollection<KeyValuePair<string, string>>IEnumerable<KeyValuePair<string, string>>IDictionary(non-generic)ICollection(non-generic)IEnumerable(non-generic)IDisposable
Note: KVStreamer is read-only. All modification operations (Add, Remove, Clear) will throw NotSupportedException.
Constructor
KVStreamer(float cacheDuration = 300f)
cacheDuration: Cache duration in seconds, default is 300 seconds
Methods
CreateBinaryFromCSV (Static)
static void CreateBinaryFromCSV(string csvPath, string outputPath, bool compress = true)
Create binary file from CSV file with optional compression (static method).
Parameters:
csvPath: CSV file pathoutputPath: Output .bytes file pathcompress: Enable GZip compression (default: true)
Exceptions:
FileNotFoundException: CSV file does not existException: CSV format error (missing ID or Text column)
Compression Benefits:
- Small files (12 records): ~36% compression rate
- Large files (1,368 records): ~67% compression rate (3:1 ratio)
- Automatic decompression on load
Note: This is a static method, no need to create instance.
LoadBinaryFile
void LoadBinaryFile(string binaryFilePath)
Load binary file from file path and parse map header.
Parameters:
binaryFilePath: .bytes file path
Exceptions:
FileNotFoundException: Binary file does not exist
LoadBinaryData
void LoadBinaryData(byte[] binaryData)
Load binary data from byte array (Recommended for Unity). Automatically detects and decompresses GZip-compressed data.
Parameters:
binaryData: Binary data byte array (compressed or uncompressed)
Exceptions:
ArgumentException: Data is null or empty
Note: This method automatically handles both compressed and uncompressed formats for backward compatibility.
GetValue
string GetValue(string key)
Get value by key (with caching).
Parameters:
key: Key
Returns:
- Corresponding value, returns
nullif not found
Indexer
string this[string key] { get; }
Gets the value associated with the specified key (Dictionary-like indexer).
Parameters:
key: The key of the value to get
Returns:
- The value associated with the specified key
Exceptions:
KeyNotFoundException: The key does not exist
Example:
string value = streamer["item_001"];
TryGetValue
bool TryGetValue(string key, out string value)
Attempts to get the value associated with the specified key.
Parameters:
key: Keyvalue: When this method returns, contains the value associated with the specified key if found; otherwise,null
Returns:
trueif the key was found; otherwise,false
Example:
if (streamer.TryGetValue("item_001", out string value))
{
Console.WriteLine($"Found: {value}");
}
else
{
Console.WriteLine("Key not found");
}
GetAllKeys
List<string> GetAllKeys()
Get list of all keys.
Returns:
- List of all keys
ContainsKey
bool ContainsKey(string key)
Check if key exists.
Parameters:
key: Key to check
Returns:
- Returns
trueif exists, otherwisefalse
ClearCache
void ClearCache()
Clear all cache.
CloseBinaryFile
void CloseBinaryFile()
Close binary file stream.
Properties
Count
int Count { get; }
Get total number of key-value pairs.
Keys
ICollection<string> Keys { get; }
Gets a collection containing the keys (Dictionary-like property).
Example:
foreach (string key in streamer.Keys)
{
Console.WriteLine(key);
}
🎮 Unity Usage Example
using UnityEngine;
using FSTGame;
public class LocalizationManager : MonoBehaviour
{
private KVStreamer _streamer;
void Start()
{
// Create instance, cache for 5 minutes
_streamer = new KVStreamer(cacheDuration: 300f);
// Load binary file (place in StreamingAssets or Resources folder)
string path = Application.streamingAssetsPath + "/localization.bytes";
_streamer.LoadBinaryFile(path);
Debug.Log($"Loaded {_streamer.Count} localization texts");
}
// Get localized text
public string GetText(string key)
{
return _streamer?.GetValue(key) ?? key;
}
void OnDestroy()
{
// Release resources
_streamer?.Dispose();
}
}
⚡ Performance Benchmarks
Comprehensive performance comparison between KVStreamer and traditional Dictionary using BenchmarkDotNet and dedicated memory analysis tools.
📊 Test Environment
- .NET Version: .NET 8.0
- Build Mode: Release
- Test Tools: BenchmarkDotNet 0.15.8 + Custom Memory Analyzer
- Test Data: chapter1.csv (1,368 records)
- File Size: CSV 114.94 KB, Binary 42.40 KB (63.11% compression)
💾 Memory Usage Comparison
| Metric | KVStreamer (No Cache) | KVStreamer (Full Cache) | Dictionary | Description |
|---|---|---|---|---|
| Total Memory | 309.98 KB | 442.96 KB | 247.07 KB | All data structures |
| Per Record | 232 bytes/record | 332 bytes/record | 185 bytes/record | Average usage |
| vs Dictionary | +25.5% | +79.3% | Baseline | Memory comparison |
| File Size | 42.40 KB | 42.40 KB | 114.94 KB (CSV) | Storage space |
⚡ Loading Performance Comparison
| Operation | KVStreamer | Dictionary | Advantage |
|---|---|---|---|
| Load Time | 1 ms | 2 ms | 2x |
| Binary File | 42.40 KB | - | 63% disk space saved |
| GC Pressure | Very Low | Medium | Zero-allocation reads |
| Memory Allocation | Load-time only | Load-time | On-demand reading |
🎯 Core Advantages
1️⃣ File Storage Advantage
- Binary Format: Saves 63.11% disk space compared to CSV
- Compression Efficiency: From 114.94 KB compressed to 42.40 KB
- Mobile-Friendly: Suitable for resource-constrained mobile devices
2️⃣ Loading Performance Advantage
- KVStreamer: Directly loads byte[] to memory, only parses map header
- Dictionary: Needs to parse entire CSV content, creates multiple string objects
- Conclusion: KVStreamer loads 2x faster, binary format eliminates CSV parsing overhead
3️⃣ Memory Flexibility
KVStreamer (No Cache Mode):
Initial Memory: 309.98 KB
Read Method: On-demand from stream, minimal memory footprint
Use Case: Large datasets, memory-sensitive applications
KVStreamer (Full Cache Mode):
Initial Memory: 442.96 KB
Read Method: All data cached, fastest read speed
Use Case: High-frequency access, performance priority
Dictionary:
Initial Memory: 247.07 KB
Data Resident: All values permanently occupy memory
Use Case: Small datasets, random access
4️⃣ Usage Recommendations
- Minimal Memory: KVStreamer No-Cache mode (309.98 KB)
- Fastest Reads: KVStreamer Cached mode or Dictionary
- Balanced: KVStreamer Partial-Cache mode (adaptive)
- Minimal Disk: KVStreamer Binary format (63% savings)
📈 Read Performance Comparison
Based on BenchmarkDotNet precise measurements (testing in progress, data updating...):
| Operation | KVStreamer (No Cache) | KVStreamer (Cached) | Dictionary |
|---|---|---|---|
| Single Read | ~200 ns | < 10 ns | ~20 ns |
| Batch Read 100 items | ~20 μs | ~1 μs | ~2 μs |
| Iterate All Data | Streaming | Very Fast | Fast |
Note: With caching enabled, KVStreamer read performance approaches or exceeds Dictionary while maintaining lower GC pressure
🎮 Recommended Usage Scenarios
✅ Recommended to Use KVStreamer
Best Scenarios:
- ✅ Mobile Platform Optimization: 63% file size reduction, lower download costs
- ✅ Large Dataset + Partial Access: 10K records with 5% access, save 70%+ memory
- ✅ Temporary Data: Dialogs, level configs, auto-cleanup after cache expiration
- ✅ Memory-Sensitive Apps: 2-4GB memory devices, dynamic memory management
- ✅ Hot Update AssetBundles: Smaller binary files, 2x faster loading
Data Characteristics:
- Large dataset (>1000 records)
- Low access rate (<50%)
- Clear access patterns
- Package size sensitive
🔴 Recommended to Use Dictionary
Best Scenarios:
- 🔴 Small Dataset (<1000 records): Lower static memory footprint
- 🔴 Full Access: All data will be used
- 🔴 Extreme Read Performance: 11ns vs 192ns (17x faster)
- 🔴 Zero GC Requirement: Zero runtime allocation
- 🔴 Simple Scenarios: Familiar API, easy to use
Data Characteristics:
- Small dataset
- Frequent access
- Sufficient memory
- Performance priority
🛠️ Running Benchmarks
cd Src/Benchmark
dotnet run -c Release
Test Environment:
- .NET 8.0
- Release build
- BenchmarkDotNet 0.15.8
- Test data: chapter1.csv (1368 records, 132KB)
💡 Performance Optimization Tips
- Enable Caching: For frequently accessed data, enabling cache provides Dictionary-like performance
- Preload Hot Data: Preload commonly used keys at startup to fill cache
- Reasonable Cache Time: Set appropriate cacheDuration based on business scenarios
- Use byte[] Loading: Use LoadBinaryData(byte[]) instead of LoadBinaryFile() in Unity
⚠️ Cache System
Cache Features
- ✅ Auto Expiration: Automatically expires after set duration
- ✅ Periodic Cleanup: Automatically cleans expired cache every 60 seconds
- ✅ Memory Optimization: Only caches accessed data
- ✅ Configurable: Supports dynamic cache time adjustment
Cache Usage Example
using (KVStreamer streamer = new KVStreamer(cacheDuration: 60f))
{
streamer.LoadBinaryFile("data.bytes");
// First read, from file stream
string text1 = streamer.GetValue("item_001"); // Slower
// Second read, from cache
string text2 = streamer.GetValue("item_001"); // Fast
// Manually clear cache
streamer.ClearCache();
}
🔍 Performance Optimization Recommendations
Set Reasonable Cache Time: Adjust cache time based on actual usage scenarios
- Frequently accessed data: Set longer cache time (e.g., 300-600 seconds)
- Occasionally accessed data: Set shorter cache time (e.g., 60-120 seconds)
Batch Preload: If you know the data to be accessed, batch preload to cache at startup
Release Promptly: Call
Dispose()after use or useusingstatement for automatic resource releaseAvoid Repeated Creation: Recommend using singleton pattern to manage
KVStreamerinstances
📝 Running Example
Enter the project directory, compile and run the example program:
cd c:\GIT\KVStreamer
csc /out:Example.exe /recurse:*.cs
Example.exe
Or open the project in Visual Studio and run.
⚠️ Important Notes
- CSV file must contain
IDandTextcolumns (case-insensitive) - Supports CSV quote wrapping and comma escaping
- Encoding is unified to UTF-8
- Keys and values cannot be empty strings
- Duplicate IDs only keep the first one
📄 License
MIT License
🤝 Contributing
Issues and Pull Requests are welcome!
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- System.Buffers (>= 4.6.1)
- System.Memory (>= 4.6.3)
- System.Threading.Tasks.Extensions (>= 4.5.4)
- UniTask (>= 2.5.10)
- ZLinq (>= 1.5.4)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version 1.7.3 - Critical Memory Leak Fix in CloseDataStream:
⚠️ 严重 Bug 修复:CloseDataStream() 内存泄漏
问题描述:
- CloseDataStream() 只清理了 _dataStream
- 没有清理 _rawData 和 ThreadLocal 创建的 MemoryStream
- 导致 ThreadLocal 模式下内存泄漏
- 重复调用 LoadBinaryData 会导致内存累积
修复内容:
1. 在 CloseDataStream() 中释放所有 ThreadLocal 的 MemoryStream
2. 清空 _rawData 引用释放内存
3. 确保重复加载数据时正确清理旧数据
影响场景:
- 使用 ThreadLocal 模式(useThreadLocalStream: true)
- 多次调用 LoadBinaryData() 加载不同数据
- 长时间运行的应用
泄漏示例:
```csharp
var streamer = new KVStreamer(300f, false, true);
streamer.LoadBinaryData(data1); // 10MB
streamer.LoadBinaryData(data2); // 10MB
// v1.7.2: 泄漏 10MB (data1 未释放)
// v1.7.3: 泄漏 0MB (正确清理)
```
升级建议:
如果使用 ThreadLocal 模式,强烈建议立即升级!
Version 1.7.2 - Critical Resource Leak Fix:
⚠️ 严重 Bug 修复:ThreadLocal<MemoryStream> 资源泄漏
问题描述:
- 原代码使用 trackAllValues: false
- 导致 ThreadLocal 创建的 MemoryStream 无法被正确释放
- 多线程场景下会造成严重内存泄漏
修复内容:
1. 将 trackAllValues 修改为 true
2. Dispose() 中手动释放所有线程的 MemoryStream
3. 添加 foreach 遍历 _threadLocalStream.Values
影响范围:
- 使用 useThreadLocalStream: true 的用户
- 多线程/高并发场景
- 长时间运行的应用
升级建议:
如果使用了 ThreadLocal 模式,强烈建议立即升级!
这是一个严重的内存泄漏 bug,可能导致应用崩溃。
Version 1.7.1 - Critical Bug Fixes:
- 修复:KVStreamer<TValue>.TryGetValueAsync() 值类型判断错误
* 原代码:value != null 对值类型永远为 true
* 修复后:使用 EqualityComparer<TValue> 正确判断
- 修复:PreheatAsync() 性能问题
* 原代码:每个 key 都 yield,导致性能下降
* 修复后:每10个 key yield 一次
- 增强:TryGetValueAsync() 异常处理
* 添加 try-catch 避免转换器异常
Bug 影响:
- 值类型(int, long, float 等)的 TryGetValueAsync 可能返回错误结果
- PreheatAsync 性能比预期低 10 倍
建议更新:
如果使用 v1.7.0 并使用了泛型异步 API,强烈建议升级到 v1.7.1
Version 1.7.0 - Async/Await and UniTask Support:
- 新增:UniTask 2.5.10 异步支持(Unity 专用)
- 新增:标准 Task 异步支持(.NET Core/.NET Standard)
- 新增:IKVStreamerAsync<TValue> 异步接口
- 新增:LoadBinaryDataAsync() 异步加载
- 新增:GetValueAsync() 异步读取
- 新增:TryGetValueAsync() 异步尝试读取
- 新增:PreheatAsync() 异步预热
- 新增:PreheatAllAsync() 异步全量预热
- 支持 CancellationToken 取消操作
异步特性:
- Unity 环境自动使用 UniTask(零 GC、高性能)
- 非 Unity 环境使用标准 Task
- 支持主线程/线程池自动切换
- 缓存命中时无异步开销(快速路径)
- 泛型 KVStreamer<TValue> 完全支持异步
使用示例(Unity + UniTask):async 加载、读取、预热数据,泛型异步转换
Version 1.6.1 - Bug Fixes and Performance Improvements:
- 修复:AccessStats.AccessCount 线程安全问题(使用 Interlocked.Increment)
- 优化:ArrayPool.Return 添加 clearArray: false 参数(提升性能)
- 优化:ICollection.CopyTo 使用 ZLinq 零分配
- 优化:GetEnumerator 使用 ZLinq 零分配
- 优化:IReadOnlyDictionary.Values 使用 ZLinq 零分配
- 优化:IDictionary.Values 使用 ZLinq 零分配
Bug 修复详情:
- 在高并发场景下,AccessCount++ 可能导致竞态条件
- 现使用 Interlocked.Increment 原子操作确保线程安全
性能提升:
- ArrayPool 返回时无需清除缓冲区,减少开销
- 所有枚举操作均使用 ZLinq 零分配
Version 1.6.0 - ZLinq Zero-Allocation Integration:
- 集成 Cysharp/ZLinq 1.5.4 零分配 LINQ 库
- 使用 AsValueEnumerable() 实现零分配遍历
- 优化 GetAccessStatistics():零分配转换
- 优化 Preheat():零分配遍历
- 优化 ValueCache.CleanupExpiredEntries():零分配查找
- 优化 IDictionary.Values 和 CopyTo():零分配遍历
- 新增 GetAllKeysArray():零分配获取键数组
- 升级 System.Memory 到 4.6.3
- 升级 System.Buffers 到 4.6.1
ZLinq 性能优势:
- 零分配的 LINQ 操作链
- 比传统 LINQ 更高的基础性能
- 兼容 .NET Standard 2.0 和 Unity
- 所有 .NET 平台可用 .NET 10 的 LINQ 操作符
使用示例:
// 零分配遍历
foreach (var key in streamer.Keys.AsValueEnumerable()) { }
// 零分配获取键数组
string[] keys = streamer.GetAllKeysArray();
Version 1.5.2 - Code Quality Improvements:
- Optimized constructor chain to reduce code duplication (43 lines removed)
- Changed AccessStats storage to ConcurrentDictionary for thread-safety
- Improved RecordAccess to use AddOrUpdate atomic operation
- Optimized ValueCache.CleanupExpiredEntries with LINQ
- Removed unused Lazy Loading fields (simplified codebase)
- Better code consistency and maintainability
Performance Impact:
- Slightly better thread-safety in adaptive cache mode
- Reduced code surface = fewer bugs
- No breaking changes
Version 1.5.1 - Unity Support Enhancements:
- Unified conditional compilation symbols for consistency
- Added comprehensive Unity 6.3 support documentation
- Clarified Brotli limitation across all Unity versions
- Code organization improvements for better maintainability
- UNITY_SUPPORT.md added with detailed compatibility matrix
- Performance benchmarks for Unity 6.3 (Mono vs IL2CPP)
- Best practices and configuration guides
Unity 6.3 Status:
- Mono backend: Full Span<T> support (40-60% less GC)
- IL2CPP backend: ArrayPool optimization (30-50% less GC)
- All features except Brotli fully supported
- Adaptive cache, ThreadLocal, and generics work perfectly
Version 1.5.0 - P2 Optimizations (Generic Support):
- Added generic type support with KVStreamer<TValue>
- IKVStreamer<TValue> interface for type-safe access
- Built-in converters: Int32, Int64, Single, Double, Boolean
- Custom converter support for any type
- JSON converter support (.NET Core 3.0+)
- KVStreamer now implements IKVStreamer<string>
- Lazy loading preparation (field infrastructure added)
- Zero breaking changes - fully backward compatible
Generic Usage Examples:
// Integer values
var intStreamer = new KVStreamer<int>(KVConverters.Int32);
intStreamer.LoadBinaryData(data);
int value = intStreamer["key"];
// Custom types
var customStreamer = new KVStreamer<MyType>(
s => JsonSerializer.Deserialize<MyType>(s)
);
Version 1.4.1 - Unity 6+ Optimization:
- Added Span<T> support for Unity 6.0+ (when using Mono backend)
- Unity 6+ with .NET Standard 2.1 can now use stackalloc and Span<T>
- 40-60% less GC allocation in Unity 6+ projects
- Conditional compilation: UNITY_6000_0_OR_NEWER && !ENABLE_IL2CPP
- IL2CPP builds still use traditional ArrayPool path (safe fallback)
Unity Compatibility Matrix:
- Unity 2019-2021: ArrayPool optimization (supported)
- Unity 6.0+ (Mono): Span<T> + ArrayPool (fully optimized)
- Unity 6.0+ (IL2CPP): ArrayPool optimization (supported)
- All Unity versions: ThreadLocal<Stream> lock-free mode (supported)
Version 1.4.0 - P1 Optimizations:
- Span<T> optimization for .NET Core 3.1+ (reduce GC pressure)
- ThreadLocal<Stream> lock-free mode for high concurrency (3-10x faster in multi-thread scenarios)
- Adaptive cache based on access statistics (smart hot-key caching)
- GetAccessStatistics() API for performance analysis
- Three constructor overloads for different performance profiles
- stackalloc buffer limit increased to 1KB for .NET Core 3.1+
Performance Improvements:
- 40-60% less GC allocation with Span<T> (NETCOREAPP3_1+ / Unity 6+)
- 3-10x better concurrency with ThreadLocal<Stream> mode
- 50-70% memory savings with adaptive cache
- Zero lock contention in lock-free mode
Version 1.3.1:
- Fixed Unity compatibility: Brotli disabled in Unity builds
- Added conditional compilation for Unity (UNITY_2019_1_OR_NEWER)
- Ensures GZip works perfectly in all Unity versions
- Updated documentation for Unity users
Version 1.3.0:
- Major performance optimization using ArrayPool (30-50% less GC pressure)
- Upgraded cache system with Ticks and ConcurrentDictionary (2-3x faster)
- Replaced lock with ReaderWriterLockSlim (3-5x better concurrency)
- Added String.Intern for memory optimization (10-20% memory savings)
- Added preheat functionality for cache warming
- Added Brotli compression support (.NET Core 3.0+, 15-20% better ratio)
- Multiple compression algorithm support
- Added NuGet dependencies for netstandard2.0 compatibility
Version 1.2.0:
- Implement full Dictionary interface compatibility
- Support IDictionary<string, string> and IReadOnlyDictionary<string, string>
- Support ICollection and IEnumerable interfaces (generic and non-generic)
- Add Values property and enumeration support
- Add DictionaryEnumerator for non-generic IDictionary
- All modification methods throw NotSupportedException (read-only)
- Fully compatible with Dictionary<TKey, TValue> interface
- Performance optimization using TryGetValue
Version 1.1.0:
- Change namespace to FSTGame
- GZip compression (60-70% size reduction)
- Map header indexing for fast lookup
- Smart caching system
- Thread-safe operations