KVStreamer 1.7.3

dotnet add package KVStreamer --version 1.7.3
                    
NuGet\Install-Package KVStreamer -Version 1.7.3
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="KVStreamer" Version="1.7.3" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="KVStreamer" Version="1.7.3" />
                    
Directory.Packages.props
<PackageReference Include="KVStreamer" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add KVStreamer --version 1.7.3
                    
#r "nuget: KVStreamer, 1.7.3"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package KVStreamer@1.7.3
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=KVStreamer&version=1.7.3
                    
Install as a Cake Addin
#tool nuget:?package=KVStreamer&version=1.7.3
                    
Install as a Cake Tool

KVStreamer

中文 | English

NuGet NuGet Downloads License: MIT

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

  1. Download the latest release from NuGet
  2. Extract the .nupkg file (rename to .zip)
  3. Copy KVStreamer.dll from lib/netstandard2.0/ to your Unity project's Plugins folder

✨ 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 path
  • outputPath: Output .bytes file path
  • compress: Enable GZip compression (default: true)

Exceptions:

  • FileNotFoundException: CSV file does not exist
  • Exception: 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 null if 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: Key
  • value: When this method returns, contains the value associated with the specified key if found; otherwise, null

Returns:

  • true if 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 true if exists, otherwise false
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

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

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

  1. Enable Caching: For frequently accessed data, enabling cache provides Dictionary-like performance
  2. Preload Hot Data: Preload commonly used keys at startup to fill cache
  3. Reasonable Cache Time: Set appropriate cacheDuration based on business scenarios
  4. 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

  1. 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)
  2. Batch Preload: If you know the data to be accessed, batch preload to cache at startup

  3. Release Promptly: Call Dispose() after use or use using statement for automatic resource release

  4. Avoid Repeated Creation: Recommend using singleton pattern to manage KVStreamer instances

📝 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

  1. CSV file must contain ID and Text columns (case-insensitive)
  2. Supports CSV quote wrapping and comma escaping
  3. Encoding is unified to UTF-8
  4. Keys and values cannot be empty strings
  5. Duplicate IDs only keep the first one

📄 License

MIT License

🤝 Contributing

Issues and Pull Requests are welcome!

Product 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.7.3 551 12/11/2025
1.2.0 532 12/10/2025
1.1.0 542 12/10/2025
1.0.0 543 12/10/2025

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