Add nullability to nucache & lucene projects

This commit is contained in:
Nikolaj Geisle
2022-03-30 13:34:56 +02:00
parent b52c4e50cf
commit 05a08bef63
105 changed files with 736 additions and 619 deletions

View File

@@ -8,7 +8,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
/// </summary>
public class ContentDataSerializer : ISerializer<ContentData>
{
public ContentDataSerializer(IDictionaryOfPropertyDataSerializer dictionaryOfPropertyDataSerializer = null)
public ContentDataSerializer(IDictionaryOfPropertyDataSerializer? dictionaryOfPropertyDataSerializer = null)
{
_dictionaryOfPropertyDataSerializer = dictionaryOfPropertyDataSerializer;
if(_dictionaryOfPropertyDataSerializer == null)
@@ -18,7 +18,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
}
private static readonly DictionaryOfPropertyDataSerializer s_defaultPropertiesSerializer = new DictionaryOfPropertyDataSerializer();
private static readonly DictionaryOfCultureVariationSerializer s_defaultCultureVariationsSerializer = new DictionaryOfCultureVariationSerializer();
private readonly IDictionaryOfPropertyDataSerializer _dictionaryOfPropertyDataSerializer;
private readonly IDictionaryOfPropertyDataSerializer? _dictionaryOfPropertyDataSerializer;
public ContentData ReadFrom(Stream stream)
{
@@ -29,7 +29,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
var versionDate = PrimitiveSerializer.DateTime.ReadFrom(stream);
var writerId = PrimitiveSerializer.Int32.ReadFrom(stream);
var templateId = PrimitiveSerializer.Int32.ReadFrom(stream);
var properties = _dictionaryOfPropertyDataSerializer.ReadFrom(stream); // TODO: We don't want to allocate empty arrays
var properties = _dictionaryOfPropertyDataSerializer?.ReadFrom(stream); // TODO: We don't want to allocate empty arrays
var cultureInfos = s_defaultCultureVariationsSerializer.ReadFrom(stream); // TODO: We don't want to allocate empty arrays
return new ContentData(name, urlSegment, versionId, versionDate, writerId, templateId, published, properties, cultureInfos);
}
@@ -43,7 +43,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
PrimitiveSerializer.DateTime.WriteTo(value.VersionDate, stream);
PrimitiveSerializer.Int32.WriteTo(value.WriterId, stream);
PrimitiveSerializer.Int32.WriteTo(value.TemplateId ?? 0, stream);
_dictionaryOfPropertyDataSerializer.WriteTo(value.Properties, stream);
_dictionaryOfPropertyDataSerializer?.WriteTo(value.Properties, stream);
s_defaultCultureVariationsSerializer.WriteTo(value.CultureInfos, stream);
}
}

View File

@@ -5,7 +5,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
{
internal class ContentNodeKitSerializer : ISerializer<ContentNodeKit>
{
public ContentNodeKitSerializer(ContentDataSerializer contentDataSerializer = null)
public ContentNodeKitSerializer(ContentDataSerializer? contentDataSerializer = null)
{
_contentDataSerializer = contentDataSerializer;
if(_contentDataSerializer == null)
@@ -14,7 +14,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
}
}
static readonly ContentDataSerializer s_defaultDataSerializer = new ContentDataSerializer();
private readonly ContentDataSerializer _contentDataSerializer;
private readonly ContentDataSerializer? _contentDataSerializer;
//static readonly ListOfIntSerializer ChildContentIdsSerializer = new ListOfIntSerializer();
@@ -33,16 +33,16 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
int contentTypeId = PrimitiveSerializer.Int32.ReadFrom(stream);
var hasDraft = PrimitiveSerializer.Boolean.ReadFrom(stream);
ContentData draftData = null;
ContentData publishedData = null;
ContentData? draftData = null;
ContentData? publishedData = null;
if (hasDraft)
{
draftData = _contentDataSerializer.ReadFrom(stream);
draftData = _contentDataSerializer?.ReadFrom(stream);
}
var hasPublished = PrimitiveSerializer.Boolean.ReadFrom(stream);
if (hasPublished)
{
publishedData = _contentDataSerializer.ReadFrom(stream);
publishedData = _contentDataSerializer?.ReadFrom(stream);
}
var kit = new ContentNodeKit(
contentNode,
@@ -55,23 +55,26 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
public void WriteTo(ContentNodeKit value, Stream stream)
{
PrimitiveSerializer.Int32.WriteTo(value.Node.Id, stream);
PrimitiveSerializer.Guid.WriteTo(value.Node.Uid, stream);
PrimitiveSerializer.Int32.WriteTo(value.Node.Level, stream);
PrimitiveSerializer.String.WriteTo(value.Node.Path, stream);
PrimitiveSerializer.Int32.WriteTo(value.Node.SortOrder, stream);
PrimitiveSerializer.Int32.WriteTo(value.Node.ParentContentId, stream);
PrimitiveSerializer.DateTime.WriteTo(value.Node.CreateDate, stream);
PrimitiveSerializer.Int32.WriteTo(value.Node.CreatorId, stream);
PrimitiveSerializer.Int32.WriteTo(value.ContentTypeId, stream);
if (value.Node is not null)
{
PrimitiveSerializer.Int32.WriteTo(value.Node.Id, stream);
PrimitiveSerializer.Guid.WriteTo(value.Node.Uid, stream);
PrimitiveSerializer.Int32.WriteTo(value.Node.Level, stream);
PrimitiveSerializer.String.WriteTo(value.Node.Path, stream);
PrimitiveSerializer.Int32.WriteTo(value.Node.SortOrder, stream);
PrimitiveSerializer.Int32.WriteTo(value.Node.ParentContentId, stream);
PrimitiveSerializer.DateTime.WriteTo(value.Node.CreateDate, stream);
PrimitiveSerializer.Int32.WriteTo(value.Node.CreatorId, stream);
PrimitiveSerializer.Int32.WriteTo(value.ContentTypeId, stream);
}
PrimitiveSerializer.Boolean.WriteTo(value.DraftData != null, stream);
if (value.DraftData != null)
_contentDataSerializer.WriteTo(value.DraftData, stream);
_contentDataSerializer?.WriteTo(value.DraftData, stream);
PrimitiveSerializer.Boolean.WriteTo(value.PublishedData != null, stream);
if (value.PublishedData != null)
_contentDataSerializer.WriteTo(value.PublishedData, stream);
_contentDataSerializer?.WriteTo(value.PublishedData, stream);
}
}
}

View File

@@ -35,7 +35,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
private static readonly IReadOnlyDictionary<string, CultureVariation> Empty = new Dictionary<string, CultureVariation>();
public void WriteTo(IReadOnlyDictionary<string, CultureVariation> value, Stream stream)
public void WriteTo(IReadOnlyDictionary<string, CultureVariation>? value, Stream stream)
{
var variations = value ?? Empty;

View File

@@ -7,7 +7,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
{
public class BTree
{
public static BPlusTree<int, ContentNodeKit> GetTree(string filepath, bool exists, NuCacheSettings settings, ContentDataSerializer contentDataSerializer = null)
public static BPlusTree<int, ContentNodeKit> GetTree(string filepath, bool exists, NuCacheSettings settings, ContentDataSerializer? contentDataSerializer = null)
{
var keySerializer = new PrimitiveSerializer();
var valueSerializer = new ContentNodeKitSerializer(contentDataSerializer);

View File

@@ -18,17 +18,17 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
[JsonProperty("pd")]
[JsonConverter(typeof(AutoInterningStringKeyCaseInsensitiveDictionaryConverter<PropertyData[]>))]
[MessagePackFormatter(typeof(MessagePackAutoInterningStringKeyCaseInsensitiveDictionaryFormatter<PropertyData[]>))]
public Dictionary<string, PropertyData[]> PropertyData { get; set; }
public Dictionary<string, PropertyData[]>? PropertyData { get; set; }
[DataMember(Order = 1)]
[JsonProperty("cd")]
[JsonConverter(typeof(AutoInterningStringKeyCaseInsensitiveDictionaryConverter<CultureVariation>))]
[MessagePackFormatter(typeof(MessagePackAutoInterningStringKeyCaseInsensitiveDictionaryFormatter<CultureVariation>))]
public Dictionary<string, CultureVariation> CultureData { get; set; }
public Dictionary<string, CultureVariation>? CultureData { get; set; }
[DataMember(Order = 2)]
[JsonProperty("us")]
public string UrlSegment { get; set; }
public string? UrlSegment { get; set; }
//Legacy properties used to deserialize existing nucache db entries
[IgnoreDataMember]

View File

@@ -9,16 +9,16 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
/// </summary>
public struct ContentCacheDataSerializationResult : IEquatable<ContentCacheDataSerializationResult>
{
public ContentCacheDataSerializationResult(string stringData, byte[] byteData)
public ContentCacheDataSerializationResult(string? stringData, byte[]? byteData)
{
StringData = stringData;
ByteData = byteData;
}
public string StringData { get; }
public byte[] ByteData { get; }
public string? StringData { get; }
public byte[]? ByteData { get; }
public override bool Equals(object obj)
public override bool Equals(object? obj)
=> obj is ContentCacheDataSerializationResult result && Equals(result);
public bool Equals(ContentCacheDataSerializationResult other)
@@ -28,8 +28,16 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
public override int GetHashCode()
{
var hashCode = 1910544615;
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(StringData);
hashCode = hashCode * -1521134295 + EqualityComparer<byte[]>.Default.GetHashCode(ByteData);
if (StringData is not null)
{
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(StringData);
}
if (ByteData is not null)
{
hashCode = hashCode * -1521134295 + EqualityComparer<byte[]>.Default.GetHashCode(ByteData);
}
return hashCode;
}

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
{
@@ -8,13 +9,17 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
/// </summary>
public class ContentData
{
// Scheduled for removal in V11
[Obsolete("Use ctor with all params, as the pros should be immutable")]
public ContentData()
{
Name = string.Empty;
UrlSegment = string.Empty;
Properties = null!;
CultureInfos = null!;
}
public ContentData(string name, string urlSegment, int versionId, DateTime versionDate, int writerId, int? templateId, bool published, IDictionary<string, PropertyData[]> properties, IReadOnlyDictionary<string, CultureVariation> cultureInfos)
public ContentData(string? name, string? urlSegment, int versionId, DateTime versionDate, int writerId, int? templateId, bool published, IDictionary<string, PropertyData[]>? properties, IReadOnlyDictionary<string, CultureVariation>? cultureInfos)
{
Name = name ?? throw new ArgumentNullException(nameof(name));
UrlSegment = urlSegment;
@@ -28,7 +33,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
}
public string Name { get; [Obsolete("Do not change this, use ctor with params and have this object immutable.")] set; }
public string UrlSegment { get; [Obsolete("Do not change this, use ctor with params and have this object immutable.")] set; }
public string? UrlSegment { get; [Obsolete("Do not change this, use ctor with params and have this object immutable.")] set; }
public int VersionId { get; [Obsolete("Do not change this, use ctor with params and have this object immutable.")] set; }
public DateTime VersionDate { get; [Obsolete("Do not change this, use ctor with params and have this object immutable.")] set; }
public int WriterId { get; [Obsolete("Do not change this, use ctor with params and have this object immutable.")] set; }
@@ -40,6 +45,6 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
/// <summary>
/// The collection of language Id to name for the content item
/// </summary>
public IReadOnlyDictionary<string, CultureVariation> CultureInfos { get; [Obsolete("Do not change this, use ctor with params and have this object immutable.")] set; }
public IReadOnlyDictionary<string, CultureVariation>? CultureInfos { get; [Obsolete("Do not change this, use ctor with params and have this object immutable.")] set; }
}
}

View File

@@ -11,7 +11,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
public int ContentTypeId { get; set; }
public int Level { get; set; }
public string Path { get; set; }
public string Path { get; set; } = string.Empty;
public int SortOrder { get; set; }
public int ParentId { get; set; }
@@ -23,25 +23,25 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
// edited data
public int VersionId { get; set; }
public string EditName { get; set; }
public string? EditName { get; set; }
public DateTime EditVersionDate { get; set; }
public int EditWriterId { get; set; }
public int EditTemplateId { get; set; }
public string EditData { get; set; }
public byte[] EditDataRaw { get; set; }
public string? EditData { get; set; }
public byte[]? EditDataRaw { get; set; }
// published data
public int PublishedVersionId { get; set; }
public string PubName { get; set; }
public string? PubName { get; set; }
public DateTime PubVersionDate { get; set; }
public int PubWriterId { get; set; }
public int PubTemplateId { get; set; }
public string PubData { get; set; }
public byte[] PubDataRaw { get; set; }
public string? PubData { get; set; }
public byte[]? PubDataRaw { get; set; }
// Explicit implementation
DateTime IReadOnlyContentBase.UpdateDate => EditVersionDate;
string IReadOnlyContentBase.Name => EditName;
string? IReadOnlyContentBase.Name => EditName;
int IReadOnlyContentBase.WriterId => EditWriterId;
}
}

View File

@@ -12,11 +12,11 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
{
[DataMember(Order = 0)]
[JsonProperty("nm")]
public string Name { get; set; }
public string? Name { get; set; }
[DataMember(Order = 1)]
[JsonProperty("us")]
public string UrlSegment { get; set; }
public string? UrlSegment { get; set; }
[DataMember(Order = 2)]
[JsonProperty("dt")]

View File

@@ -14,7 +14,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
/// <summary>
/// Deserialize the data into a <see cref="ContentCacheDataModel"/>
/// </summary>
ContentCacheDataModel Deserialize(IReadOnlyContentBase content, string stringData, byte[] byteData, bool published);
ContentCacheDataModel? Deserialize(IReadOnlyContentBase content, string? stringData, byte[]? byteData, bool published);
/// <summary>
/// Serializes the <see cref="ContentCacheDataModel"/>

View File

@@ -24,13 +24,13 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
DateFormatString = "o"
};
private readonly JsonNameTable _propertyNameTable = new DefaultJsonNameTable();
public ContentCacheDataModel Deserialize(IReadOnlyContentBase content, string stringData, byte[] byteData, bool published)
public ContentCacheDataModel? Deserialize(IReadOnlyContentBase content, string? stringData, byte[]? byteData, bool published)
{
if (stringData == null && byteData != null)
throw new NotSupportedException($"{typeof(JsonContentNestedDataSerializer)} does not support byte[] serialization");
JsonSerializer serializer = JsonSerializer.Create(_jsonSerializerSettings);
using (JsonTextReader reader = new JsonTextReader(new StringReader(stringData)))
using (JsonTextReader reader = new JsonTextReader(new StringReader(stringData!)))
{
// reader will get buffer from array pool
reader.ArrayPool = JsonArrayPool.Instance;
@@ -58,12 +58,16 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
return ArrayPool<char>.Shared.Rent(minimumLength);
}
public void Return(char[] array)
public void Return(char[]? array)
{
// return char array to System.Buffers shared pool
ArrayPool<char>.Shared.Return(array);
if (array is not null)
{
ArrayPool<char>.Shared.Return(array);
}
}
}
public class AutomaticJsonNameTable : DefaultJsonNameTable
{
int nAutoAdded = 0;
@@ -74,7 +78,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
this.maxToAutoAdd = maxToAdd;
}
public override string Get(char[] key, int start, int length)
public override string? Get(char[] key, int start, int length)
{
var s = base.Get(key, start, length);

View File

@@ -12,8 +12,8 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
[DebuggerDisplay("{Display}")]
internal struct LazyCompressedString
{
private byte[] _bytes;
private string _str;
private byte[]? _bytes;
private string? _str;
private readonly object _locker;
/// <summary>

View File

@@ -49,7 +49,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
return json;
}
public ContentCacheDataModel Deserialize(IReadOnlyContentBase content, string stringData, byte[] byteData, bool published)
public ContentCacheDataModel? Deserialize(IReadOnlyContentBase content, string? stringData, byte[]? byteData, bool published)
{
if (byteData != null)
{
@@ -93,17 +93,26 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
/// </remarks>
private void Compress(IReadOnlyContentBase content, ContentCacheDataModel model, bool published)
{
if (model.PropertyData is null)
{
return;
}
foreach(var propertyAliasToData in model.PropertyData)
{
if (_propertyOptions.IsCompressed(content, propertyAliasToData.Key, published))
{
foreach(var property in propertyAliasToData.Value.Where(x => x.Value != null && x.Value is string))
{
property.Value = LZ4Pickler.Pickle(Encoding.UTF8.GetBytes((string)property.Value), LZ4Level.L00_FAST);
if (property.Value is string propertyValue)
{
property.Value = LZ4Pickler.Pickle(Encoding.UTF8.GetBytes(propertyValue), LZ4Level.L00_FAST);
}
}
foreach (var property in propertyAliasToData.Value.Where(x => x.Value != null && x.Value is int intVal))
{
property.Value = Convert.ToBoolean((int)property.Value);
property.Value = Convert.ToBoolean((int?)property.Value);
}
}
}
@@ -117,6 +126,11 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
/// <param name="published"></param>
private void Expand(IReadOnlyContentBase content, ContentCacheDataModel nestedData, bool published)
{
if (nestedData.PropertyData is null)
{
return;
}
foreach (var propertyAliasToData in nestedData.PropertyData)
{
if (_propertyOptions.IsCompressed(content, propertyAliasToData.Key,published))

View File

@@ -10,14 +10,14 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
[DataContract] // NOTE: Use DataContract annotations here to control how MessagePack serializes/deserializes the data to use INT keys
public class PropertyData
{
private string _culture;
private string _segment;
private string? _culture;
private string? _segment;
[DataMember(Order = 0)]
[JsonConverter(typeof(AutoInterningStringConverter))]
[DefaultValue("")]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate, PropertyName = "c")]
public string Culture
public string? Culture
{
get => _culture;
set => _culture = value ?? throw new ArgumentNullException(nameof(value)); // TODO: or fallback to string.Empty? CANNOT be null
@@ -27,7 +27,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
[JsonConverter(typeof(AutoInterningStringConverter))]
[DefaultValue("")]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate, PropertyName = "s")]
public string Segment
public string? Segment
{
get => _segment;
set => _segment = value ?? throw new ArgumentNullException(nameof(value)); // TODO: or fallback to string.Empty? CANNOT be null
@@ -35,7 +35,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
[DataMember(Order = 2)]
[JsonProperty("v")]
public object Value { get; set; }
public object? Value { get; set; }
// Legacy properties used to deserialize existing nucache db entries
[IgnoreDataMember]

View File

@@ -43,7 +43,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
return read(stream);
}
protected string ReadStringObject(Stream stream, bool intern = false) // required 'cos string is not a struct
protected string? ReadStringObject(Stream stream, bool intern = false) // required 'cos string is not a struct
{
var type = PrimitiveSerializer.Char.ReadFrom(stream);
if (type == PrefixNull) return null;
@@ -60,7 +60,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
protected double? ReadDoubleObject(Stream stream) => ReadStruct(stream, PrefixDouble, ReadDouble);
protected DateTime? ReadDateTimeObject(Stream stream) => ReadStruct(stream, PrefixDateTime, ReadDateTime);
protected object ReadObject(Stream stream)
protected object? ReadObject(Stream stream)
=> ReadObject(PrimitiveSerializer.Char.ReadFrom(stream), stream);
/// <summary>
@@ -73,7 +73,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
/// This will incur boxing because the result is an object but in most cases the value will be a struct.
/// When the type is known use the specific methods like <see cref="ReadInt(Stream)"/> instead
/// </remarks>
protected object ReadObject(char type, Stream stream)
protected object? ReadObject(char type, Stream stream)
{
switch (type)
{
@@ -127,7 +127,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
/// This method will incur boxing if the value is a struct. When the type is known use the <see cref="PrimitiveSerializer"/>
/// to write the value directly.
/// </remarks>
protected void WriteObject(object value, Stream stream)
protected void WriteObject(object? value, Stream stream)
{
if (value == null)
{