Skip to content

[duplicate-code] Duplicate Code: BaseSerializer.cs Dual #if NETCOREAPP / #else Primitive Read/Write Implementations #9765

Description

@github-actions

Summary

src/Platform/Microsoft.Testing.Platform/IPC/Serializers/BaseSerializer.cs implements every read/write primitive twice — once under #if NETCOREAPP using Span<byte>, ArrayPool<byte>, and ReadExactly, and once under #else using heap-allocated byte[] and stream.Read. The two implementations are structurally identical; the only differences are the allocation strategy and the ReadExactly vs manual-read pattern. The codebase already ships polyfills for these exact APIs in src/Polyfills/.

Duplication Details

Pattern: Dual #if NETCOREAPP / #else primitive read/write implementations

  • Severity: Medium
  • Occurrences: 12 methods duplicated (~87 #else lines mirroring ~140 #if NETCOREAPP lines)
  • Location: src/Platform/Microsoft.Testing.Platform/IPC/Serializers/BaseSerializer.cs (lines 30–257)
Method #if NETCOREAPP lines #else lines
ReadString 31–46 172–181
ReadStringValue 48–60 183–188
WriteString 62–83 191–197
WriteSize<T> 85–97 199–205
WriteInt 99–106 207–211
ReadInt 108–113 213–218
WriteLong 115–121 220–224
ReadLong 123–128 232–237
WriteUShort 130–136 226–230
ReadUShort 138–143 239–244
WriteBool 145–151 246–250
ReadBool 153–168 252–257

Example — WriteInt (signatures are 100% identical; only the body differs):

#if NETCOREAPP (stack-allocated Span<byte>):

protected static void WriteInt(Stream stream, int value)
{
    Span<byte> bytes = stackalloc byte[sizeof(int)];
    if (!BitConverter.TryWriteBytes(bytes, value))
    {
        throw Unreachable();
    }
    stream.Write(bytes);
}

#else (heap-allocated byte[]):

protected static void WriteInt(Stream stream, int value)
{
    byte[] bytes = BitConverter.GetBytes(value);
    stream.Write(bytes, 0, bytes.Length);
}

Example — ReadString:

#if NETCOREAPP uses ArrayPool<byte>.Shared.Rent/Return + stream.ReadExactly.
#else uses new byte[length] + stream.Read(bytes, 0, bytes.Length).

Impact Analysis

  • Maintainability: Adding a new primitive (e.g., ReadUInt, WriteFloat) requires writing it twice. Any bug in a read/write primitive must be fixed in both branches.
  • Bug Risk: The #else branch's stream.Read calls do not assert/retry on short reads, while ReadExactly in #if NETCOREAPP does — a subtle behavioral divergence that could surface as data corruption on .NET Framework in low-level scenarios.
  • Code Bloat: ~87 lines of duplicated code in the #else branch alone.

Refactoring Recommendations

  1. Use existing polyfills: The src/Polyfills/ directory already contains shims for Span<byte> and related APIs. Add or verify polyfills for:

    • Stream.ReadExactly(Span<byte>) — already shimmable via ReadExactly(byte[], int, int) plus a loop
    • BitConverter.TryWriteBytes(Span<byte>, int) — can be shimmed with BitConverter.GetBytes + Array.Copy
    • stream.Write(ReadOnlySpan<byte>) — can be shimmed with stream.Write(byte[], 0, length)
  2. Collapse to a single implementation once the polyfills are in place, retaining the #if NETCOREAPP performance-optimized path as optional (or removing the #else branch entirely if .NET Framework is no longer a supported target for this assembly).

  3. Fix the stream.Read short-read risk in the #else branch as a priority independent of the above, since it can produce silent data corruption.

Implementation Checklist

  • Review duplication findings
  • Check which TFMs Microsoft.Testing.Platform must support on .NET Framework
  • Add/verify polyfill shims for ReadExactly, BitConverter.TryWriteBytes, Span<T> write
  • Collapse BaseSerializer.cs to a single implementation using polyfills
  • Verify all IPC/serializer tests pass under all target frameworks
  • Fix the stream.Read short-read issue in the #else branch as an independent safety fix if the branch must remain

Analysis Metadata

  • Analyzed Files: 1 (BaseSerializer.cs)
  • Detection Method: Semantic code analysis
  • Analysis Date: 2026-07-09

🤖 Automated content by GitHub Copilot. Generated by the Duplicate Code Detector workflow. · 273.3 AIC · ⌖ 9.1 AIC · ⊞ 8K · [◷]( · )

Add this agentic workflows to your repo

To install this agentic workflow, run

gh aw add githubnext/agentics/workflows/duplicate-code-detector.md@main
  • expires on Jul 11, 2026, 5:42 AM UTC

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions