[LOADED_DOCS: 3 files, no new loads]

Rename ShallowCopy to FlatCopy, add polymorph support

- Renamed all "ShallowCopy" serializer presets and references to "FlatCopy" for clarity and consistency.
- Expanded documentation to clarify flat serialization use cases, especially for delta-update and partial-write scenarios.
- Added EnablePolymorphDetectFeature to AcBinarySerializableAttribute and updated all constructor overloads.
- Set UsePolymorphType = true in AcBinarySourceGenerator to enable polymorphic type support by default.
- Updated all [AcBinarySerializable(...)] usages to include new feature flags, explicitly disabling property filter and polymorph detection for affected types.
- Improved comments and documentation for maintainability.
This commit is contained in:
Loretta 2026-05-15 08:40:52 +02:00
parent a7f2d3605b
commit 89618c1d10
9 changed files with 52 additions and 19 deletions

View File

@ -43,7 +43,7 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
// ────────────────────────────────────────────────────────────────────────────────────────────
// UsePropertyFilter const removed — replaced by `[AcBinarySerializable(EnablePropertyFilterFeature = ...)]`
// attribute flag, propagated through SerializableClassInfo.EnablePropertyFilter to EmitProp/EmitScanProp.
private const bool UsePolymorphType = false;
private const bool UsePolymorphType = true;
private static readonly DiagnosticDescriptor CircularReferenceWarning = new(
id: "ACBIN001",

View File

@ -31,26 +31,22 @@ public sealed class AcBinarySerializableAttribute : Attribute
/// per-property filtering for this specific type.</para>
/// </summary>
public bool EnablePropertyFilterFeature { get; }
public bool EnablePolymorphDetectFeature { get; }
public AcBinarySerializableAttribute() : this(true)
{
}
public AcBinarySerializableAttribute(bool enableAllFeatures)
{
EnableMetadataFeature = enableAllFeatures;
EnableIdTrackingFeature = enableAllFeatures;
EnableRefHandlingFeature = enableAllFeatures;
EnableInternStringFeature = enableAllFeatures;
EnablePropertyFilterFeature = enableAllFeatures;
}
public AcBinarySerializableAttribute(bool enableAllFeatures) : this(enableAllFeatures, enableAllFeatures, enableAllFeatures, enableAllFeatures, enableAllFeatures, enableAllFeatures)
{ }
public AcBinarySerializableAttribute(bool enableMetadataFeature, bool enableIdTrackingFeature, bool enableRefHandlingFeature, bool enableInternStringFeature, bool enablePropertyFilterFeature)
public AcBinarySerializableAttribute(bool enableMetadataFeature, bool enableIdTrackingFeature, bool enableRefHandlingFeature, bool enableInternStringFeature, bool enablePropertyFilterFeature, bool enablePolymorphDetectFeature)
{
EnableMetadataFeature = enableMetadataFeature;
EnableIdTrackingFeature = enableIdTrackingFeature;
EnableRefHandlingFeature = enableRefHandlingFeature;
EnableInternStringFeature = enableInternStringFeature;
EnablePropertyFilterFeature = enablePropertyFilterFeature;
EnablePolymorphDetectFeature = enablePolymorphDetectFeature;
}
}

View File

@ -34,10 +34,10 @@ public sealed class AcBinarySerializerOptions : AcSerializerOptions
};
/// <summary>
/// Options for shallow serialization (root level only).
/// Options for flat serialization (root level only).
/// Returns a new instance each time to prevent shared state corruption.
/// </summary>
public static AcBinarySerializerOptions ShallowCopy => new()
public static AcBinarySerializerOptions FlatCopy => new()
{
MaxDepth = 0,
// Truncate preserves the original "root + Null nested" semantic; under default Throw the preset

View File

@ -93,7 +93,7 @@ Key wire-format options: `WireMode` (Compact/Fast), `ReferenceHandling` (None/On
`ReferenceHandling=None` + `UseStringInterning=None` = no scan pass (single-phase, fastest).
Presets: `Default`, `FastMode`, `ShallowCopy`, `WasmOptimized`. Details: `docs/BINARY/BINARY_OPTIONS.md`.
Presets: `Default`, `FastMode`, `FlatCopy`, `WasmOptimized`. Details: `docs/BINARY/BINARY_OPTIONS.md`.
## Dependencies

View File

@ -16,10 +16,10 @@ public sealed class AcJsonSerializerOptions : AcSerializerOptions
public static AcJsonSerializerOptions Default => new() { ReferenceHandling = ReferenceHandlingMode.All };
/// <summary>
/// Options for shallow serialization (root level only, no references).
/// Options for flat serialization (root level only, no references).
/// Returns a new instance each time to prevent shared state corruption.
/// </summary>
public static AcJsonSerializerOptions ShallowCopy => new() { MaxDepth = 0, ReferenceHandling = ReferenceHandlingMode.None };
public static AcJsonSerializerOptions FlatCopy => new() { MaxDepth = 0, ReferenceHandling = ReferenceHandlingMode.None, MaxDepthBehavior = MaxDepthBehavior.Truncate};
/// <summary>
/// Creates options with specified max depth.

View File

@ -40,7 +40,7 @@ Custom JSON serialization/deserialization built on `System.Text.Json`'s `Utf8Jso
| `MaxDepth` | Maximum object graph depth |
| `ThrowOnCircularReference` | Throw vs silently handle circular refs |
**Presets:** `Default` (with refs), `ShallowCopy`, `WithMaxDepth`, `WithoutReferenceHandling`.
**Presets:** `Default` (with refs), `FlatCopy`, `WithMaxDepth`, `WithoutReferenceHandling`.
## Dependencies

View File

@ -196,7 +196,7 @@ The shallow-serialization use case is a legitimate, common pattern (client edits
- `Truncate` — the previous `WriteByte(Null)` behavior, now explicit opt-in for shallow serialization (delta updates, view-model projections, partial DB-update flows). The wire `Null` at the truncation boundary is the developer's contract decision — endpoint protocol dictates what nested null means. Works with any persistence layer (Dapper, ADO.NET, Cosmos DB, MongoDB, Redis, EF Core, etc.).
- `Disable` — skip the depth check entirely (max perf, dev guarantees cycle-free graph).
The check moved from "every object/collection write" (with rewind) to "before any marker byte is written" (in `WriteObject` runtime + `WriteObjectFullMarker*` SGen). The `ShallowCopy` preset was updated to explicitly set `MaxDepthBehavior = Truncate` to preserve its original "root + Null nested" semantic. See `BINARY_OPTIONS.md` `MaxDepth + MaxDepthBehavior` section for full details.
The check moved from "every object/collection write" (with rewind) to "before any marker byte is written" (in `WriteObject` runtime + `WriteObjectFullMarker*` SGen). The `FlatCopy` preset was updated to explicitly set `MaxDepthBehavior = Truncate` to preserve its original "root + Null nested" semantic. See `BINARY_OPTIONS.md` `MaxDepth + MaxDepthBehavior` section for full details.
### ACCORE-BIN-I-W3F4: PropertyFilter + UseMetadata=false silently corrupts via index drift

View File

@ -146,14 +146,14 @@ delegate PropertyInfo? PropertyMapperDelegate(PropertyInfo sourceProperty, Type
|--------|----------|----------|-----------------|-------------|----------|------------------|-------------|-------|
| `Default` | Compact | false | Attribute | All | 255 | Throw | None | — |
| `FastMode` | Compact | false | None | None | 255 | Throw | None | No scan pass |
| `ShallowCopy` | Compact | false | None | None | **0** | **Truncate** ⚠️ | None | Root + Null nested (the Truncate behavior makes this preset's semantic meaningful — under default `Throw` it would throw on first nested object) |
| `FlatCopy` | Compact | false | None | None | **0** | **Truncate** ⚠️ | None | Root + Null nested (the Truncate behavior makes this preset's semantic meaningful — under default `Throw` it would throw on first nested object) |
| `WasmOptimized` | Compact | false | Attribute | All | 255 | Throw | None | +StringCaching |
| `WithoutReferenceHandling` | Compact | false | Attribute | **None** | 255 | Throw | None | No scan pass |
| `WithoutMetadata` | Compact | **false** | Attribute | All | 255 | Throw | None | — |
**Performance implication of presets:**
- `Default` / `WasmOptimized` — two-phase (scan + serialize) due to `ReferenceHandling=All`
- `FastMode` / `ShallowCopy` — single-phase (no scan pass) since both interning and refs are disabled
- `FastMode` / `FlatCopy` — single-phase (no scan pass) since both interning and refs are disabled
- The scan pass adds ~20-30% overhead; disable it when the object graph is a simple tree
## Option Interactions

File diff suppressed because one or more lines are too long