Files
UnrealEngine/Engine/Plugins/Experimental/PlainProps/Source/Private/PlainPropsInternalDiff.h
2025-05-18 13:04:45 +08:00

179 lines
4.1 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "PlainPropsBind.h"
#include "PlainPropsTypes.h"
#include "Math/PreciseFP.h"
namespace PlainProps
{
inline const uint8* At(const void* Ptr, SIZE_T Offset)
{
return static_cast<const uint8*>(Ptr) + Offset;
}
// Works with FBoolRangeView, which lack GetData(), TRangeView, TArrayView, std::initializer_list and T[]
template<class RangeTypeA, class RangeTypeB>
bool DiffItems(RangeTypeA&& A, RangeTypeB&& B, uint64& OutIdx)
{
uint64 NumA = IntCastChecked<uint64>(GetNum(A));
uint64 NumB = IntCastChecked<uint64>(GetNum(B));
OutIdx = 0;
auto BIt = std::begin(B);
for (auto bA : A)
{
if (OutIdx >= NumB)
{
break;
}
if (bA != *BIt++)
{
return true;
}
++OutIdx;
}
return NumA != NumB;
}
inline int64 DiffLeaf(const uint8* A, const uint8* B, FUnpackedLeafBindType Leaf)
{
bool bBitfield = Leaf.Type == ELeafBindType::BitfieldBool;
bool bFloat = Leaf.Type == ELeafBindType::Float;
if (bBitfield | bFloat)
{
if (bBitfield)
{
return ((*A ^ *B) >> Leaf.BitfieldIdx) & 1;
}
switch (Leaf.Width)
{
case ELeafWidth::B32: return !UE::PreciseFPEqual(reinterpret_cast<const float&>(*A), reinterpret_cast<const float&>(*B));
case ELeafWidth::B64: return !UE::PreciseFPEqual(reinterpret_cast<const double&>(*A), reinterpret_cast<const double&>(*B));
default: break;
}
}
else
{
switch (Leaf.Width)
{
case ELeafWidth::B8: return FMemory::Memcmp(A, B, 1);
case ELeafWidth::B16: return FMemory::Memcmp(A, B, 2);
case ELeafWidth::B32: return FMemory::Memcmp(A, B, 4);
case ELeafWidth::B64: return FMemory::Memcmp(A, B, 8);
}
}
unimplemented();
return 0;
}
inline ELeafWidth GetItemWidth(FLeafBindType Leaf)
{
checkf(Leaf.Bind.Type != ELeafBindType::BitfieldBool, TEXT("Range of bitfields is illegal"));
return Leaf.Basic.Width;
}
////////////////////////////////////////////////////////////////////////////////////////////////
// Helps diff ranges
struct FItemRangeReader
{
explicit FItemRangeReader(const void* Range, const IItemRangeBinding& Binding)
: Ctx{{Range}}
{
ReadItems(Binding);
}
FSaveRangeContext Ctx;
const uint8* SliceIt = nullptr;
uint64 SliceNum = 0;
void ReadItems(const IItemRangeBinding& Binding)
{
Binding.ReadItems(Ctx);
SliceIt = static_cast<const uint8*>(Ctx.Items.Slice.Data);
SliceNum = Ctx.Items.Slice.Num;
}
void RefillItems(const IItemRangeBinding& Binding)
{
if (SliceNum == 0)
{
ReadItems(Binding);
check(SliceNum > 0);
}
}
const uint8* GrabItems(uint64 Num, uint32 Stride)
{
const uint8* Out = SliceIt;
SliceIt += Num * Stride;
SliceNum -= Num;
return Out;
}
};
template<class ContextType>
inline bool DiffItemSlice(const uint8* A, const uint8* B, uint64 Num, uint32 Stride, ContextType&, SIZE_T LeafSize)
{
return !!FMemory::Memcmp(A, B, Num * Stride);
}
template<typename ItemType, class ContextType>
bool DiffItemSlice(const uint8* A, const uint8* B, uint64 Num, uint32 Stride, ContextType& Ctx, ItemType&& Member)
{
for (const uint8* EndA = A + Num * Stride; A != EndA; A += Stride, B += Stride)
{
if (DiffItem(A, B, Ctx, Member))
{
return true;
}
}
return false;
}
template<class T, class ContextType>
bool DiffItemRange(const void* RangeA, const void* RangeB, const IItemRangeBinding& Binding, ContextType& OuterCtx, T&& ItemSchema)
{
FItemRangeReader A(RangeA, Binding);
FItemRangeReader B(RangeB, Binding);
if (A.Ctx.Items.NumTotal != B.Ctx.Items.NumTotal)
{
return true;
}
if (const uint64 NumTotal = A.Ctx.Items.NumTotal)
{
check(A.Ctx.Items.Stride == B.Ctx.Items.Stride);
const uint32 Stride = A.Ctx.Items.Stride;
while (true)
{
uint64 Num = FMath::Min(A.SliceNum, B.SliceNum);
if (DiffItemSlice(A.GrabItems(Num, Stride), B.GrabItems(Num, Stride), Num, Stride, OuterCtx, ItemSchema))
{
return true;
}
else if (A.Ctx.Request.NumRead + Num >= NumTotal)
{
check(A.Ctx.Request.NumRead + Num == NumTotal);
check(B.Ctx.Request.NumRead + Num == NumTotal);
return false;
}
A.Ctx.Request.NumRead += Num;
B.Ctx.Request.NumRead += Num;
A.RefillItems(Binding);
B.RefillItems(Binding);
}
}
return false;
}
} // namespace PlainProps