mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-25 06:45:18 +00:00
* Simplify validation, compression heuristics, and notification scheduling * Consolidate notification logic and add InputValidator monitoring Follow-up improvements to address PR feedback: 1. Consolidate notification functions - Add interruptionLevel parameter to sendLocalNotification - Refactor sendNetworkAvailableNotification to use consolidated function - Removes 12 lines of duplicate code 2. Add monitoring to InputValidator - Log control character rejections for production monitoring - Privacy-preserving: logs length + count, not actual content - Uses .security category for proper log routing 3. Add comprehensive InputValidator tests - 28 test cases covering validation, control characters, unicode, edge cases - Ensures behavioral changes are well-tested and documented --------- Co-authored-by: jack <jackjackbits@users.noreply.github.com>
74 lines
2.9 KiB
Swift
74 lines
2.9 KiB
Swift
//
|
|
// CompressionUtil.swift
|
|
// bitchat
|
|
//
|
|
// This is free and unencumbered software released into the public domain.
|
|
// For more information, see <https://unlicense.org>
|
|
//
|
|
|
|
import Foundation
|
|
import Compression
|
|
|
|
struct CompressionUtil {
|
|
// Compression threshold - don't compress if data is smaller than this
|
|
static let compressionThreshold = TransportConfig.compressionThresholdBytes // bytes
|
|
|
|
// Compress data using zlib algorithm (most compatible)
|
|
static func compress(_ data: Data) -> Data? {
|
|
// Skip compression for small data
|
|
guard data.count >= compressionThreshold else { return nil }
|
|
|
|
let maxCompressedSize = data.count + (data.count / 255) + 16
|
|
let destinationBuffer = UnsafeMutablePointer<UInt8>.allocate(capacity: maxCompressedSize)
|
|
defer { destinationBuffer.deallocate() }
|
|
|
|
let compressedSize = data.withUnsafeBytes { sourceBuffer in
|
|
guard let sourcePtr = sourceBuffer.bindMemory(to: UInt8.self).baseAddress else { return 0 }
|
|
return compression_encode_buffer(
|
|
destinationBuffer, data.count,
|
|
sourcePtr, data.count,
|
|
nil, COMPRESSION_ZLIB
|
|
)
|
|
}
|
|
|
|
guard compressedSize > 0 && compressedSize < data.count else { return nil }
|
|
|
|
return Data(bytes: destinationBuffer, count: compressedSize)
|
|
}
|
|
|
|
// Decompress zlib compressed data
|
|
static func decompress(_ compressedData: Data, originalSize: Int) -> Data? {
|
|
let destinationBuffer = UnsafeMutablePointer<UInt8>.allocate(capacity: originalSize)
|
|
defer { destinationBuffer.deallocate() }
|
|
|
|
let decompressedSize = compressedData.withUnsafeBytes { sourceBuffer in
|
|
guard let sourcePtr = sourceBuffer.bindMemory(to: UInt8.self).baseAddress else { return 0 }
|
|
return compression_decode_buffer(
|
|
destinationBuffer, originalSize,
|
|
sourcePtr, compressedData.count,
|
|
nil, COMPRESSION_ZLIB
|
|
)
|
|
}
|
|
|
|
guard decompressedSize > 0 else { return nil }
|
|
|
|
return Data(bytes: destinationBuffer, count: decompressedSize)
|
|
}
|
|
|
|
// Helper to check if compression is worth it
|
|
static func shouldCompress(_ data: Data) -> Bool {
|
|
// Don't compress if:
|
|
// 1. Data is too small
|
|
// 2. Data appears to be already compressed (high entropy)
|
|
guard data.count >= compressionThreshold else { return false }
|
|
|
|
// Quick uniqueness check — a high diversity of bytes usually means the
|
|
// payload is already compressed. We only need to know how many unique
|
|
// values exist rather than keeping full frequency counts.
|
|
let uniqueByteCount = Set(data).count
|
|
let sampleSize = min(data.count, 256)
|
|
let uniqueByteRatio = Double(uniqueByteCount) / Double(sampleSize)
|
|
return uniqueByteRatio < 0.9 // Compress if less than 90% unique bytes
|
|
}
|
|
}
|