[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 = ...)]` // UsePropertyFilter const removed — replaced by `[AcBinarySerializable(EnablePropertyFilterFeature = ...)]`
// attribute flag, propagated through SerializableClassInfo.EnablePropertyFilter to EmitProp/EmitScanProp. // 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( private static readonly DiagnosticDescriptor CircularReferenceWarning = new(
id: "ACBIN001", id: "ACBIN001",

View File

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

View File

@ -34,10 +34,10 @@ public sealed class AcBinarySerializerOptions : AcSerializerOptions
}; };
/// <summary> /// <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. /// Returns a new instance each time to prevent shared state corruption.
/// </summary> /// </summary>
public static AcBinarySerializerOptions ShallowCopy => new() public static AcBinarySerializerOptions FlatCopy => new()
{ {
MaxDepth = 0, MaxDepth = 0,
// Truncate preserves the original "root + Null nested" semantic; under default Throw the preset // 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). `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 ## Dependencies

View File

@ -16,10 +16,10 @@ public sealed class AcJsonSerializerOptions : AcSerializerOptions
public static AcJsonSerializerOptions Default => new() { ReferenceHandling = ReferenceHandlingMode.All }; public static AcJsonSerializerOptions Default => new() { ReferenceHandling = ReferenceHandlingMode.All };
/// <summary> /// <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. /// Returns a new instance each time to prevent shared state corruption.
/// </summary> /// </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> /// <summary>
/// Creates options with specified max depth. /// 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 | | `MaxDepth` | Maximum object graph depth |
| `ThrowOnCircularReference` | Throw vs silently handle circular refs | | `ThrowOnCircularReference` | Throw vs silently handle circular refs |
**Presets:** `Default` (with refs), `ShallowCopy`, `WithMaxDepth`, `WithoutReferenceHandling`. **Presets:** `Default` (with refs), `FlatCopy`, `WithMaxDepth`, `WithoutReferenceHandling`.
## Dependencies ## 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.). - `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). - `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 ### 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 | — | | `Default` | Compact | false | Attribute | All | 255 | Throw | None | — |
| `FastMode` | Compact | false | None | None | 255 | Throw | None | No scan pass | | `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 | | `WasmOptimized` | Compact | false | Attribute | All | 255 | Throw | None | +StringCaching |
| `WithoutReferenceHandling` | Compact | false | Attribute | **None** | 255 | Throw | None | No scan pass | | `WithoutReferenceHandling` | Compact | false | Attribute | **None** | 255 | Throw | None | No scan pass |
| `WithoutMetadata` | Compact | **false** | Attribute | All | 255 | Throw | None | — | | `WithoutMetadata` | Compact | **false** | Attribute | All | 255 | Throw | None | — |
**Performance implication of presets:** **Performance implication of presets:**
- `Default` / `WasmOptimized` — two-phase (scan + serialize) due to `ReferenceHandling=All` - `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 - The scan pass adds ~20-30% overhead; disable it when the object graph is a simple tree
## Option Interactions ## Option Interactions

File diff suppressed because one or more lines are too long