using System.Diagnostics;
using System.Text;
using K4os.Compression.LZ4;
using Umbraco.Cms.Core.Exceptions;
namespace Umbraco.Cms.Infrastructure.HybridCache.Serialization;
///
/// Lazily decompresses a LZ4 Pickler compressed UTF8 string
///
[DebuggerDisplay("{Display}")]
internal struct LazyCompressedString
{
private readonly object _locker;
private byte[]? _bytes;
private string? _str;
///
/// Constructor
///
/// LZ4 Pickle compressed UTF8 String
public LazyCompressedString(byte[] bytes)
{
_locker = new object();
_bytes = bytes;
_str = null;
}
///
/// Used to display debugging output since ToString() can only be called once
///
private string Display
{
get
{
if (_str != null)
{
return $"Decompressed: {_str}";
}
lock (_locker)
{
if (_str != null)
{
// double check
return $"Decompressed: {_str}";
}
if (_bytes == null)
{
// This shouldn't happen
throw new PanicException("Bytes have already been cleared");
}
return $"Compressed Bytes: {_bytes.Length}";
}
}
}
public static implicit operator string(LazyCompressedString l) => l.ToString();
public byte[] GetBytes()
{
if (_bytes == null)
{
throw new InvalidOperationException("The bytes have already been expanded");
}
return _bytes;
}
///
/// Returns the decompressed string from the bytes. This methods can only be called once.
///
///
/// Throws if this is called more than once
public string DecompressString()
{
if (_str != null)
{
return _str;
}
lock (_locker)
{
if (_str != null)
{
// double check
return _str;
}
if (_bytes == null)
{
throw new InvalidOperationException("Bytes have already been cleared");
}
_str = Encoding.UTF8.GetString(LZ4Pickler.Unpickle(_bytes));
_bytes = null;
}
return _str;
}
public override string ToString() => DecompressString();
}