How Can We Help?
Array Compression Functions (deflate)
Overview
The Compress array (deflate) and Decompress array (deflate) functions provide high-performance data compression and decompression for byte arrays using the deflate algorithm. These functions are perfect for reducing memory usage, network transmission, and storage requirements for large data sets.
Function Descriptions
Compress Array (deflate)
- Node Name: Compress array (deflate)
- Category: Archive Manager
- Type: Synchronous (immediate operation)
Compresses a byte array using the deflate compression algorithm, returning the compressed data along with the original size for decompression reference.
Decompress Array (deflate)
- Node Name: Decompress array (deflate)
- Category: Archive Manager
- Type: Synchronous (immediate operation)
Decompresses a previously compressed byte array back to its original form using the original size information.
Blueprint Usage
Compress Array (deflate) Parameters
Parameter | Type | Description |
---|---|---|
Input | Array of Bytes | The original byte array to compress |
Output | Type | Description |
---|---|---|
Output | Array of Bytes | The compressed byte array |
Original Size | Integer | Size of the original data (needed for decompression) |
Return Value | Boolean | true if compression succeeded, false if failed |
Decompress Array (deflate) Parameters
Parameter | Type | Description |
---|---|---|
Input | Array of Bytes | The compressed byte array to decompress |
Original Size | Integer | Size of the original uncompressed data |
Output | Type | Description |
---|---|---|
Output | Array of Bytes | The decompressed byte array (original data) |
Return Value | Boolean | true if decompression succeeded, false if failed |
Blueprint Setup Example

Example compression/Decompression Workflow
- Create Test Data: Use “Create random data” node to generate sample data
- Set Size: 1000 (creates 1000 bytes of random data)
- Display Original Size: Connect to Print String to show original data size
- Compress Data: Add “Compress array (deflate)” node
- Connect random data array to Input
- Store the Original Size output for later decompression
- Display Compression Results: Show compressed size and compression ratio
- Decompress Data: Add “Decompress array (deflate)” node
- Connect compressed Output to Input
- Connect stored Original Size to Original Size parameter
- Verify Results: Display decompressed data size to confirm successful round-trip
C++ Usage
Basic Compression / Decompression
void BasicCompressionExample()
{
// Create some test data
TArray<uint8> OriginalData;
FString TestString = TEXT("Hello World! This is a test string for compression.");
// Convert string to byte array
FTCHARToUTF8 Converter(*TestString);
OriginalData.Append((uint8*)Converter.Get(), Converter.Length());
// Compress the data
TArray<uint8> CompressedData;
int32 OriginalSize;
bool bCompressionSuccess = UArchiveManagerBPLibrary::CompressArray(
OriginalData,
CompressedData,
OriginalSize
);
if (bCompressionSuccess)
{
UE_LOG(LogTemp, Log, TEXT("Compression successful. Original: %d bytes, Compressed: %d bytes"),
OriginalSize, CompressedData.Num());
// Calculate compression ratio
float CompressionRatio = (float)CompressedData.Num() / (float)OriginalSize;
UE_LOG(LogTemp, Log, TEXT("Compression ratio: %.2f (%.1f%% savings)"),
CompressionRatio, (1.0f - CompressionRatio) * 100.0f);
// Decompress the data
TArray<uint8> DecompressedData;
bool bDecompressionSuccess = UArchiveManagerBPLibrary::DecompressArray(
CompressedData,
OriginalSize,
DecompressedData
);
if (bDecompressionSuccess)
{
UE_LOG(LogTemp, Log, TEXT("Decompression successful. Size: %d bytes"),
DecompressedData.Num());
// Verify data integrity
if (DecompressedData.Num() == OriginalData.Num())
{
bool bDataMatches = true;
for (int32 i = 0; i < OriginalData.Num(); i++)
{
if (OriginalData[i] != DecompressedData[i])
{
bDataMatches = false;
break;
}
}
if (bDataMatches)
{
UE_LOG(LogTemp, Log, TEXT("✅ Data integrity verified - round-trip successful!"));
}
else
{
UE_LOG(LogTemp, Error, TEXT("❌ Data integrity check failed!"));
}
}
}
else
{
UE_LOG(LogTemp, Error, TEXT("❌ Decompression failed!"));
}
}
else
{
UE_LOG(LogTemp, Error, TEXT("❌ Compression failed!"));
}
}
Network Data Compression System
UCLASS()
class YOURGAME_API UNetworkCompressionManager : public UObject
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable)
TArray<uint8> CompressForNetwork(const TArray<uint8>& Data, int32& OutOriginalSize);
UFUNCTION(BlueprintCallable)
TArray<uint8> DecompressFromNetwork(const TArray<uint8>& CompressedData, int32 OriginalSize);
UFUNCTION(BlueprintCallable)
float GetCompressionRatio(int32 OriginalSize, int32 CompressedSize);
UFUNCTION(BlueprintCallable)
bool ShouldCompressData(int32 DataSize, float MinCompressionRatio = 0.8f);
};
TArray<uint8> UNetworkCompressionManager::CompressForNetwork(const TArray<uint8>& Data, int32& OutOriginalSize)
{
TArray<uint8> CompressedData;
// Only compress if data is large enough to benefit
if (Data.Num() < 100)
{
UE_LOG(LogTemp, Log, TEXT("Data too small for compression (%d bytes), sending uncompressed"), Data.Num());
OutOriginalSize = Data.Num();
return Data;
}
bool bSuccess = UArchiveManagerBPLibrary::CompressArray(Data, CompressedData, OutOriginalSize);
if (bSuccess)
{
float CompressionRatio = GetCompressionRatio(OutOriginalSize, CompressedData.Num());
// Only use compressed data if it's actually smaller
if (CompressionRatio < 0.95f) // At least 5% savings
{
UE_LOG(LogTemp, Log, TEXT("Network compression: %.2f%% size reduction (%d -> %d bytes)"),
(1.0f - CompressionRatio) * 100.0f, OutOriginalSize, CompressedData.Num());
return CompressedData;
}
else
{
UE_LOG(LogTemp, Log, TEXT("Compression not beneficial (%.2f ratio), sending uncompressed"), CompressionRatio);
}
}
else
{
UE_LOG(LogTemp, Warning, TEXT("Compression failed, sending uncompressed data"));
}
OutOriginalSize = Data.Num();
return Data; // Return original if compression failed or wasn't beneficial
}
TArray<uint8> UNetworkCompressionManager::DecompressFromNetwork(const TArray<uint8>& CompressedData, int32 OriginalSize)
{
// If sizes match, data wasn't compressed
if (CompressedData.Num() == OriginalSize)
{
UE_LOG(LogTemp, Log, TEXT("Data wasn't compressed, returning as-is"));
return CompressedData;
}
TArray<uint8> DecompressedData;
bool bSuccess = UArchiveManagerBPLibrary::DecompressArray(CompressedData, OriginalSize, DecompressedData);
if (bSuccess)
{
UE_LOG(LogTemp, Log, TEXT("Network decompression successful: %d -> %d bytes"),
CompressedData.Num(), DecompressedData.Num());
return DecompressedData;
}
else
{
UE_LOG(LogTemp, Error, TEXT("Network decompression failed! Returning empty array"));
return TArray<uint8>();
}
}
float UNetworkCompressionManager::GetCompressionRatio(int32 OriginalSize, int32 CompressedSize)
{
return OriginalSize > 0 ? (float)CompressedSize / (float)OriginalSize : 1.0f;
}
bool UNetworkCompressionManager::ShouldCompressData(int32 DataSize, float MinCompressionRatio)
{
// Don't compress very small data
if (DataSize < 100) return false;
// For this example, we assume compression will be beneficial for larger data
// In practice, you might want to test compression on a sample first
return DataSize > 500;
}
Save Game Compression System
UCLASS()
class YOURGAME_API UCompressedSaveGame : public USaveGame
{
GENERATED_BODY()
public:
UPROPERTY(SaveGame)
TArray<uint8> CompressedGameData;
UPROPERTY(SaveGame)
int32 OriginalDataSize;
UPROPERTY(SaveGame)
bool bIsCompressed;
UPROPERTY(SaveGame)
FString SaveVersion;
UPROPERTY(SaveGame)
FDateTime SaveTime;
// Compress and store game data
UFUNCTION(BlueprintCallable)
bool CompressAndStore(const TArray<uint8>& GameData);
// Decompress and retrieve game data
UFUNCTION(BlueprintCallable)
bool DecompressAndRetrieve(TArray<uint8>& OutGameData);
// Get compression statistics
UFUNCTION(BlueprintCallable)
FString GetCompressionStats();
};
bool UCompressedSaveGame::CompressAndStore(const TArray<uint8>& GameData)
{
SaveTime = FDateTime::Now();
SaveVersion = TEXT("1.0");
// Try to compress the data
bool bSuccess = UArchiveManagerBPLibrary::CompressArray(
GameData,
CompressedGameData,
OriginalDataSize
);
if (bSuccess)
{
// Check if compression was beneficial
float CompressionRatio = (float)CompressedGameData.Num() / (float)OriginalDataSize;
if (CompressionRatio < 0.9f) // At least 10% savings
{
bIsCompressed = true;
UE_LOG(LogTemp, Log, TEXT("Save game compressed: %d -> %d bytes (%.1f%% savings)"),
OriginalDataSize,
CompressedGameData.Num(),
(1.0f - CompressionRatio) * 100.0f);
}
else
{
// Store uncompressed if compression wasn't beneficial
bIsCompressed = false;
CompressedGameData = GameData;
UE_LOG(LogTemp, Log, TEXT("Save game stored uncompressed (compression not beneficial)"));
}
}
else
{
// Store uncompressed if compression failed
bIsCompressed = false;
CompressedGameData = GameData;
OriginalDataSize = GameData.Num();
UE_LOG(LogTemp, Warning, TEXT("Compression failed, storing uncompressed save data"));
}
return true; // Always succeed in storing, even if compression failed
}
bool UCompressedSaveGame::DecompressAndRetrieve(TArray<uint8>& OutGameData)
{
if (!bIsCompressed)
{
// Data wasn't compressed, return as-is
OutGameData = CompressedGameData;
UE_LOG(LogTemp, Log, TEXT("Save game wasn't compressed, returning %d bytes"), OutGameData.Num());
return true;
}
// Decompress the data
bool bSuccess = UArchiveManagerBPLibrary::DecompressArray(
CompressedGameData,
OriginalDataSize,
OutGameData
);
if (bSuccess)
{
UE_LOG(LogTemp, Log, TEXT("Save game decompressed: %d -> %d bytes"),
CompressedGameData.Num(), OutGameData.Num());
}
else
{
UE_LOG(LogTemp, Error, TEXT("Save game decompression failed!"));
}
return bSuccess;
}
FString UCompressedSaveGame::GetCompressionStats()
{
if (!bIsCompressed)
{
return FString::Printf(TEXT("Uncompressed: %d bytes"), CompressedGameData.Num());
}
float CompressionRatio = (float)CompressedGameData.Num() / (float)OriginalDataSize;
float SavingsPercent = (1.0f - CompressionRatio) * 100.0f;
return FString::Printf(TEXT("Compressed: %d -> %d bytes (%.1f%% savings)"),
OriginalDataSize, CompressedGameData.Num(), SavingsPercent);
}
Asset Cache Compression
UCLASS()
class YOURGAME_API UAssetCacheManager : public UObject
{
GENERATED_BODY()
private:
struct FCachedAssetData
{
TArray<uint8> CompressedData;
int32 OriginalSize;
FString AssetPath;
FDateTime CacheTime;
bool bIsCompressed;
FCachedAssetData()
: OriginalSize(0)
, bIsCompressed(false)
{
}
};
TMap<FString, FCachedAssetData> AssetCache;
public:
UFUNCTION(BlueprintCallable)
bool CacheAssetData(const FString& AssetPath, const TArray<uint8>& AssetData);
UFUNCTION(BlueprintCallable)
bool RetrieveAssetData(const FString& AssetPath, TArray<uint8>& OutAssetData);
UFUNCTION(BlueprintCallable)
void ClearCache();
UFUNCTION(BlueprintCallable)
TArray<FString> GetCachedAssetPaths();
UFUNCTION(BlueprintCallable)
FString GetCacheStats();
};
bool UAssetCacheManager::CacheAssetData(const FString& AssetPath, const TArray<uint8>& AssetData)
{
FCachedAssetData CacheEntry;
CacheEntry.AssetPath = AssetPath;
CacheEntry.CacheTime = FDateTime::Now();
// Try to compress the asset data
bool bSuccess = UArchiveManagerBPLibrary::CompressArray(
AssetData,
CacheEntry.CompressedData,
CacheEntry.OriginalSize
);
if (bSuccess)
{
float CompressionRatio = (float)CacheEntry.CompressedData.Num() / (float)CacheEntry.OriginalSize;
// Only use compression if it's beneficial
if (CompressionRatio < 0.8f) // At least 20% savings
{
CacheEntry.bIsCompressed = true;
UE_LOG(LogTemp, Log, TEXT("Cached asset %s: %d bytes compressed to %d bytes (%.1f%% savings)"),
*AssetPath, CacheEntry.OriginalSize, CacheEntry.CompressedData.Num(),
(1.0f - CompressionRatio) * 100.0f);
}
else
{
// Store uncompressed if compression wasn't beneficial
CacheEntry.bIsCompressed = false;
CacheEntry.CompressedData = AssetData;
UE_LOG(LogTemp, Log, TEXT("Cached asset %s: stored uncompressed (%d bytes)"),
*AssetPath, AssetData.Num());
}
}
else
{
// Store uncompressed if compression failed
CacheEntry.bIsCompressed = false;
CacheEntry.CompressedData = AssetData;
CacheEntry.OriginalSize = AssetData.Num();
UE_LOG(LogTemp, Warning, TEXT("Compression failed for asset %s, storing uncompressed"), *AssetPath);
}
AssetCache.Add(AssetPath, CacheEntry);
return true;
}
bool UAssetCacheManager::RetrieveAssetData(const FString& AssetPath, TArray<uint8>& OutAssetData)
{
FCachedAssetData* CacheEntry = AssetCache.Find(AssetPath);
if (!CacheEntry)
{
UE_LOG(LogTemp, Warning, TEXT("Asset %s not found in cache"), *AssetPath);
return false;
}
if (!CacheEntry->bIsCompressed)
{
// Data wasn't compressed, return as-is
OutAssetData = CacheEntry->CompressedData;
UE_LOG(LogTemp, Log, TEXT("Retrieved uncompressed asset %s (%d bytes)"),
*AssetPath, OutAssetData.Num());
return true;
}
// Decompress the cached data
bool bSuccess = UArchiveManagerBPLibrary::DecompressArray(
CacheEntry->CompressedData,
CacheEntry->OriginalSize,
OutAssetData
);
if (bSuccess)
{
UE_LOG(LogTemp, Log, TEXT("Retrieved and decompressed asset %s: %d -> %d bytes"),
*AssetPath, CacheEntry->CompressedData.Num(), OutAssetData.Num());
}
else
{
UE_LOG(LogTemp, Error, TEXT("Failed to decompress cached asset %s"), *AssetPath);
}
return bSuccess;
}
void UAssetCacheManager::ClearCache()
{
int32 NumCleared = AssetCache.Num();
AssetCache.Empty();
UE_LOG(LogTemp, Log, TEXT("Cleared %d cached assets"), NumCleared);
}
TArray<FString> UAssetCacheManager::GetCachedAssetPaths()
{
TArray<FString> Paths;
AssetCache.GetKeys(Paths);
return Paths;
}
FString UAssetCacheManager::GetCacheStats()
{
if (AssetCache.Num() == 0)
{
return TEXT("Cache is empty");
}
int32 TotalOriginalSize = 0;
int32 TotalCompressedSize = 0;
int32 CompressedCount = 0;
for (const auto& Entry : AssetCache)
{
TotalOriginalSize += Entry.Value.OriginalSize;
TotalCompressedSize += Entry.Value.CompressedData.Num();
if (Entry.Value.bIsCompressed)
{
CompressedCount++;
}
}
float OverallCompressionRatio = TotalOriginalSize > 0 ?
(float)TotalCompressedSize / (float)TotalOriginalSize : 1.0f;
return FString::Printf(TEXT("Cache: %d assets, %d compressed, %d -> %d bytes (%.1f%% savings)"),
AssetCache.Num(), CompressedCount, TotalOriginalSize, TotalCompressedSize,
(1.0f - OverallCompressionRatio) * 100.0f);
}
Utility functions for common operations
namespace ArchiveManagerUtils
{
// Convert FString to byte array for compression
TArray<uint8> StringToByteArray(const FString& String)
{
TArray<uint8> ByteArray;
FTCHARToUTF8 Converter(*String);
ByteArray.Append((uint8*)Converter.Get(), Converter.Length());
return ByteArray;
}
// Convert byte array back to FString after decompression
FString ByteArrayToString(const TArray<uint8>& ByteArray)
{
FUTF8ToTCHAR Converter((ANSICHAR*)ByteArray.GetData(), ByteArray.Num());
return FString(Converter.Length(), Converter.Get());
}
// Test compression on a sample to determine if it's worth doing
bool TestCompressionBenefit(const TArray<uint8>& Data, float MinSavingsPercent = 10.0f)
{
if (Data.Num() < 100) return false; // Too small to benefit
TArray<uint8> CompressedData;
int32 OriginalSize;
bool bSuccess = UArchiveManagerBPLibrary::CompressArray(Data, CompressedData, OriginalSize);
if (!bSuccess) return false;
float CompressionRatio = (float)CompressedData.Num() / (float)OriginalSize;
float SavingsPercent = (1.0f - CompressionRatio) * 100.0f;
return SavingsPercent >= MinSavingsPercent;
}
// Compress string data
bool CompressString(const FString& InputString, TArray<uint8>& OutCompressedData, int32& OutOriginalSize)
{
TArray<uint8> StringData = StringToByteArray(InputString);
return UArchiveManagerBPLibrary::CompressArray(StringData, OutCompressedData, OutOriginalSize);
}
// Decompress back to string
bool DecompressString(const TArray<uint8>& CompressedData, int32 OriginalSize, FString& OutString)
{
TArray<uint8> DecompressedData;
bool bSuccess = UArchiveManagerBPLibrary::DecompressArray(CompressedData, OriginalSize, DecompressedData);
if (bSuccess)
{
OutString = ByteArrayToString(DecompressedData);
}
return bSuccess;
}
}
Error handling and validation
void ErrorHandlingExample()
{
UE_LOG(LogTemp, Warning, TEXT("=== Testing Error Handling ==="));
// Test 1: Empty array compression
{
TArray<uint8> EmptyData;
TArray<uint8> CompressedData;
int32 OriginalSize;
bool bResult = UArchiveManagerBPLibrary::CompressArray(EmptyData, CompressedData, OriginalSize);
UE_LOG(LogTemp, Log, TEXT("Empty array compression: %s (OriginalSize: %d)"),
bResult ? TEXT("SUCCESS") : TEXT("FAILED"), OriginalSize);
}
// Test 2: Invalid decompression with negative size
{
TArray<uint8> TestData = {1, 2, 3, 4, 5};
TArray<uint8> CompressedData;
int32 ValidOriginalSize;
if (UArchiveManagerBPLibrary::CompressArray(TestData, CompressedData, ValidOriginalSize))
{
TArray<uint8> InvalidDecompressed;
bool bResult = UArchiveManagerBPLibrary::DecompressArray(CompressedData, -1, InvalidDecompressed);
UE_LOG(LogTemp, Log, TEXT("Negative size decompression: %s"),
bResult ? TEXT("UNEXPECTED SUCCESS") : TEXT("CORRECTLY FAILED"));
}
}
// Test 3: Invalid decompression with zero size
{
TArray<uint8> TestData = {1, 2, 3, 4, 5};
TArray<uint8> CompressedData;
int32 ValidOriginalSize;
if (UArchiveManagerBPLibrary::CompressArray(TestData, CompressedData, ValidOriginalSize))
{
TArray<uint8> InvalidDecompressed;
bool bResult = UArchiveManagerBPLibrary::DecompressArray(CompressedData, 0, InvalidDecompressed);
UE_LOG(LogTemp, Log, TEXT("Zero size decompression: %s"),
bResult ? TEXT("UNEXPECTED SUCCESS") : TEXT("CORRECTLY FAILED"));
}
}
// Test 4: Try to decompress random data
{
TArray<uint8> RandomData = {0xFF, 0xAA, 0x55, 0x00, 0x99, 0x77, 0x33, 0x11};
TArray<uint8> FakeDecompressed;
bool bResult = UArchiveManagerBPLibrary::DecompressArray(RandomData, 1000, FakeDecompressed);
UE_LOG(LogTemp, Log, TEXT("Random data decompression: %s"),
bResult ? TEXT("UNEXPECTED SUCCESS") : TEXT("CORRECTLY FAILED"));
}
UE_LOG(LogTemp, Warning, TEXT("=== Error Handling Tests Complete ==="));
}
Advanced Features
Compression Efficiency
The deflate algorithm provides excellent compression for:
- Text Data: Configuration files, JSON, XML (60-80% compression)
- Repetitive Data: Level data, tile maps (40-70% compression)
- Save Game Data: Player progress, inventory (30-60% compression)
- Network Messages: Large data packets (varies by content type)
Performance Characteristics
- Fast Compression: Optimized for real-time applications
- Low Memory Overhead: Minimal temporary memory allocation
- Predictable Performance: Linear time complexity with data size
- Thread Safe: Can be used from any thread
Data Type Compatibility
Works with any data that can be represented as byte arrays:
- Structs: Convert with FMemoryWriter/FMemoryReader
- Strings: Convert with StringToBytes utilities
- Images: Raw pixel data compression
- Audio: PCM audio data compression
- Custom Data: Any serializable game data
Common Use Cases
Game Development
- Save Game Compression: Reduce save file sizes by 30-70%
- Level Data Storage: Compress procedural or user-generated content
- Asset Caching: Store frequently-used assets in compressed form
- Network Optimization: Reduce bandwidth usage for large data transfers
Application Development
- Configuration Data: Compress settings and preference files
- Document Storage: Reduce storage requirements for text data
- Cache Management: Store compressed data in memory caches
- Backup Systems: Compress data before archiving
Best Practices
When to Use Compression
- Large data sets: Benefits increase with data size (>1KB recommended)
- Repetitive content: Text, structured data, level data
- Storage optimization: When disk/memory space is limited
- Network transmission: When bandwidth is constrained
When to Avoid Compression
- Already compressed data: Images (JPEG/PNG), audio (MP3/OGG), video
- Very small data: <100 bytes may not benefit from compression
- Random data: Truly random data doesn’t compress well
- Real-time critical paths: When CPU cycles are more valuable than storage
Memory Management
- Store original size: Always preserve the original size for decompression
- Validate inputs: Check array sizes before compression/decompression
- Handle failures: Always check return values for success
- Consider trade-offs: Balance compression ratio vs. processing time
Error Handling
- Validate compressed data: Ensure data isn’t corrupted before decompression
- Size validation: Verify original size parameter is reasonable
- Fallback strategies: Have plans for when compression/decompression fails
- Logging: Log compression ratios and failures for debugging
Compression Performance Tips
Optimal Data Preparation
- Sort similar data: Group similar values together for better compression
- Remove redundancy: Eliminate duplicate information before compression
- Use appropriate data types: Smaller data types compress better
- Batch operations: Compress larger chunks rather than many small pieces
Memory Optimization
- Reuse arrays: Pre-allocate output arrays when possible
- Stream large data: For very large datasets, consider chunked processing
- Monitor memory usage: Track compression overhead in memory-constrained environments
Integration Examples
Blueprint Save System Integration
Custom Event: Save Game
↓
Convert Save Data to Bytes
↓
Compress array (deflate)
↓
Store Compressed Data + Original Size
↓
Print String: "Save compressed: X% size reduction"
Network Message Compression
Custom Event: Send Large Message
↓
Convert Message to Bytes
↓
Branch: Is Data > 500 bytes?
↓ (True)
Compress array (deflate)
↓
Send Compressed Data + Original Size + Compression Flag
↓ (False)
Send Original Data + No Compression Flag
Common Errors
Compression Failures
- “Compression failed”: Input data may be corrupted or empty
- Solution: Validate input array has data before compression
Decompression Failures
- “Decompression failed”: Compressed data is corrupted or original size is wrong
- Solution: Verify compressed data integrity and correct original size
Size Mismatches
- “Original size mismatch”: Provided original size doesn’t match actual data
- Solution: Ensure original size is stored correctly during compression
Memory Issues
- “Out of memory”: Insufficient memory for decompression buffer
- Solution: Verify original size is reasonable and memory is available