Skip to content

feat(phase-1): frame codec — TcrMessage / TcrPart / BigEndian{Reader,Writer} - #7

Merged
TomiCheng merged 4 commits into
developfrom
feat/phase-1-frame-codec
May 8, 2026
Merged

TomiCheng merged 4 commits into
developfrom
feat/phase-1-frame-codec

Conversation

@TomiCheng

Copy link
Copy Markdown
Owner

Summary

Phase 1 frame codec — pure encode/decode of TcrMessage ↔ bytes. All MVP wire-format primitives in place. No sockets yet.

Phase / Roadmap

Changes

Phase 1 codec (src/Geode.Client/Protocol/)

  • MessageType — enum mirroring cppcache/src/TcrMessage.hpp::enum MsgType in full (99 wire values + 2 sentinels). PascalCase, dropped redundant _MSG_TYPE / _TYPE suffixes. Numeric gaps (57, 95, 101, 102, 104) preserved.
  • BigEndianBinaryWriter — sequential big-endian writer over an internal MemoryStream. C# counterpart of cppcache DataOutput. Implemented: WriteByte / WriteBool / WriteInt32 / WriteInt64 / WriteBytesOnly. Stubs (NotImplementedException with phase markers) for WriteSByte / WriteInt16 / WriteUInt16/32/64 / WriteFloat / WriteDouble / WriteBytes / WriteArrayLen / WriteJavaModifiedUtf8 / WriteUtf16Huge.
  • BigEndianBinaryReader — symmetric reader over ReadOnlyMemory<byte>, with zero-copy ReadBytesOnly. Throws EndOfStreamException on overrun.
  • TcrPart record — i32 length + u8 IsObject + ReadOnlyMemory<byte> Payload. Custom Equals / GetHashCode for byte-content equality (record default would be reference-based on ReadOnlyMemory).
  • TcrMessage record — 17-byte header (msgType / msgLength / numParts / txId / earlyAck) + Parts. Two-pass Encode (parts first to learn length, then header + parts). Decode strict-validates MessageLength matches actual parts byte count and NumParts >= 0.
  • Removed _Phase1Placeholder.cs.

Repo plumbing carried on this branch

  • NuGet.config<clear/> + nuget.org only, fixes NU1507 on machines with extra sources configured at user / machine level (CPM requires either single source or explicit mapping).
  • Directory.Build.propsAnalysisLevel relaxed from latest-recommended to latest-default during MVP. TODO marker to tighten back before Phase 5 / first NuGet release. Fixed CHANGE_ME → real GitHub URL.
  • samples/.../Program.cs — added missing using Microsoft.Extensions.DependencyInjection; (CS1061 fix).
  • tests/.../GeodeFixture.cs[SuppressMessage("Naming", "CA1711")] on GeodeCollection (xUnit [CollectionDefinition(nameof(...))] convention requires the class name).
  • geode-dotnet.sln — VS 2022 added src / test / sample solution folders and version stamps.
  • Dropped FluentAssertions across the repo. v8.x changed to a non-OSI Xceed license; instead of auditing the new terms for our Apache-2.0 use case, removed the dependency entirely. xUnit native Assert.* covers everything we used. Phase 0 SmokeTests / GeodeContainerSmokeTests also converted.
  • Routine dependency bumps via VS auto-update: Microsoft.NET.Test.Sdk 17.12 → 18.5.1, xunit.v3 1.0 → 3.2.2, xunit.runner.visualstudio 3.0 → 3.1.5, coverlet.collector 6.0.2 → 10.0.0.
  • .gitignore — ignore .cr/ (VS 2022 extension cache).

Tests

  • Unit tests — 4 new files under tests/Geode.Client.Tests/Protocol/ (~34 facts):
    • BigEndianBinaryWriterTests — primitives, concat, length tracking.
    • BigEndianBinaryReaderTests — primitives, zero-copy slice (verified by mutating source array), bounds, position tracking.
    • TcrPartTests — round-trip (simple / empty / isObject), validation, content-based equality.
    • TcrMessageTests — round-trip + byte-fixture tests for Ping (17 bytes) and Put-with-byte-part (23 bytes), derived from cppcache wire format (cite writeHeader / writeMessageLength / writeBytePart line numbers in code comments). Validation: negative NumParts, MessageLength mismatch.
  • New / updated integration tests — N/A for Phase 1 (no socket yet).
  • Existing tests still pass — Phase 0 SmokeTests + GeodeContainerSmokeTests now use Assert.* after FA removal.

Notes for reviewer

  • Buffer-based codec design committed long-term. Reader takes ReadOnlyMemory<byte>, Writer owns internal buffer + ToArray(). Aligns with modern .NET codec patterns (System.Text.Json, MessagePack-CSharp, System.IO.Pipelines): codec is sync over Memory/Span; async lives at the I/O boundary (Phase 2's TcpConnection).
  • CI is currently disabled (see CONTRIBUTING.md §5.3); local VS build was the only validation. Re-enable target: before Phase 5 / first NuGet preview.
  • This branch carries non-Phase-1 chore commits (analyzer relax, NuGet config, FA removal, deps). They surfaced during Phase 1 development and were not worth a separate branch given solo-dev / CI-off context. Future feature branches will be tighter.
  • Phase 0 has a known wire-format claim about Part containing a separate Type byte — verified against cppcache/src/TcrMessage.cpp line 812-816 that Part is 3 fields: length / isObject / payload (the C++ Type is the first byte of payload when isObject=1). This implementation matches the actual cppcache code.

Tomi and others added 4 commits May 8, 2026 21:36
Five small changes to get the Phase 0 skeleton building cleanly on a
developer machine that has both nuget.org and a corporate feed
configured, and to stop opinionated analyzer rules from blocking
walking-skeleton work:

- NuGet.config: <clear/> + nuget.org only, fixes NU1507 caused by
  Central Package Management with multiple inherited sources.
- Directory.Build.props: AnalysisLevel latest-recommended -> latest-default
  during MVP. TODO marker to tighten back before Phase 5 / first NuGet
  release.
- samples/Geode.Client.Sample/Program.cs: add missing
  'using Microsoft.Extensions.DependencyInjection;' so GetRequiredService
  resolves (it is an extension method on IServiceProvider in that ns).
- tests/.../GeodeFixture.cs: SuppressMessage CA1711 on GeodeCollection.
  xUnit's [CollectionDefinition(nameof(...))] convention uses the class
  name as the collection identifier; renaming would break the call sites.
- .gitignore: ignore .cr/ (Visual Studio extension cache).

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Visual Studio 2022 added these on first open:
- VisualStudioVersion / MinimumVisualStudioVersion stamps.
- 'src', 'test', 'sample' solution folders nesting the four projects
  for a tidier Solution Explorer view.
- Solution items entries re-sorted alphabetically.

Pure IDE metadata — dotnet CLI ignores solution folders, so no effect on
build, restore, or CI.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Foundation layer for the Phase 1 frame codec.

- MessageType: full enum mirroring cppcache TcrMessage.hpp::MsgType
  (99 values incl. -2/-1 sentinels and the 4 numeric gaps preserved).
  Naming: SCREAMING_SNAKE_CASE -> PascalCase, _MSG_TYPE / _TYPE
  redundant suffixes dropped (e.g. EXECUTECQ_MSG_TYPE -> ExecuteCq).

- BigEndianBinaryWriter: sequential big-endian writer over an internal
  MemoryStream. C# counterpart of cppcache DataOutput. Phase 1 implements
  WriteByte / WriteBool / WriteInt32 / WriteInt64 / WriteBytesOnly /
  ToArray / Length; the rest (WriteSByte, WriteInt16, WriteUInt16/32/64,
  WriteFloat, WriteDouble, WriteBytes, WriteArrayLen,
  WriteJavaModifiedUtf8, WriteUtf16Huge) are prototype stubs that throw
  NotImplementedException so the API surface is stable across phases.

- BigEndianBinaryReader: sequential big-endian reader over a
  ReadOnlyMemory<byte>. Symmetric stub set. ReadBytesOnly returns a
  zero-copy slice. EndOfStreamException on overrun.

- TcrPart: record (i32 length + u8 isObject + raw payload) modelling the
  inline 3-step encoding used by every cppcache TcrMessage::write*Part
  helper. Equals / GetHashCode overridden so equality is byte-content
  based (record default would be reference-based on ReadOnlyMemory).

Buffer-based design (reader takes ReadOnlyMemory, writer owns internal
buffer) committed as the long-term shape — matches modern .NET codec
patterns (System.Text.Json, MessagePack-CSharp, Pipelines) where async
lives at the I/O boundary and the codec itself is sync over Memory/Span.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Code:
- TcrMessage record (header + Parts) with two-pass Encode and strict
  Decode validation. Mirrors cppcache TcrMessage::writeHeader /
  handleByteArrayResponse / writeMessageLength.
- Custom Equals/GetHashCode so Parts list compares element-wise
  (record default would be reference equality on the list).
- Drop _Phase1Placeholder.cs now that the real Protocol/ files exist.

Tests (xUnit native Assert, no FluentAssertions):
- Protocol/BigEndianBinaryWriterTests  — 8 facts: primitives, concat,
  length tracking.
- Protocol/BigEndianBinaryReaderTests  — 9 facts: primitives, zero-copy
  slice (proven by mutating source), bounds, position tracking.
- Protocol/TcrPartTests                — 7 facts: round-trip
  (simple / empty / isObject), validation, content-based equality.
- Protocol/TcrMessageTests             — 10 facts: round-trip, Ping and
  Put-with-byte-part byte fixtures derived from cppcache wire format,
  malformed-frame validation, element-wise Parts equality.

FluentAssertions removed:
- v8.x switched to a custom (non-OSI) Xceed license; rather than audit
  the new terms for our Apache-2.0 use case, drop the dependency
  entirely. xUnit native Assert.* covers everything we used.
- Existing Phase 0 tests (SmokeTests, GeodeContainerSmokeTests) also
  converted from .Should() to Assert.*, so the codebase has zero FA
  references.
- Removed from Directory.Packages.props and from both test csprojs.

Routine dependency bumps (accepted while VS auto-updated them):
- Microsoft.NET.Test.Sdk     17.12.0 -> 18.5.1
- xunit.v3                    1.0.0  -> 3.2.2
- xunit.runner.visualstudio   3.0.0  -> 3.1.5
- coverlet.collector          6.0.2  -> 10.0.0

Geode.Client.Tests.csproj also picked up <PrivateAssets>all</PrivateAssets>
+ <IncludeAssets> on xunit.runner.visualstudio and coverlet.collector —
the standard NuGet pattern for dev-only packages, kept as-is.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@TomiCheng
TomiCheng merged commit 757349b into develop May 8, 2026
@TomiCheng
TomiCheng deleted the feat/phase-1-frame-codec branch May 8, 2026 14:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Phase 1: Implement frame codec (encode/decode TcrMessage)

1 participant