Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.2.1] - 2026-04-25

### Fixed
- **Unsuffixed `{Message}Data` alias collapsed to baseline V0 instead of widest version** (#149): The `effectiveVersion` widening logic introduced in v1.1.4 (#143) was inadvertently dropped while wiring the per-version blockLength compute for the new `{Msg}VersionMap` (#146). Restored: the unsuffixed canonical struct again includes every field added across the schema's version history (matches the natural reader expectation and v1.1.4 behavior). The per-version `{Msg}V{N}Data` aliases and the `{Msg}VersionMap` entries continue to use each version's own blockLength.

## [1.2.0] - 2026-04-25

### Added
Expand Down
20 changes: 15 additions & 5 deletions src/SbeCodeGenerator/Generators/MessagesCodeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,17 @@ internal class MessagesCodeGenerator : ICodeGenerator
foreach (var version in versions)
{
var versionNamespace = GetVersionNamespace(baseNamespace, ns, version);
var fieldsForVersion = GetFieldsForVersion(messageDto.Fields, version, sourceContext, context);

// For the canonical/unsuffixed struct (version 0 file), include all fields up to the
// schema version so the unsuffixed alias is the widest struct, matching #143 and the
// natural reader expectation ("give me everything the schema can tell me about this
// message"). The per-version blockLength used by the VersionMap is computed below
// using each version's *own* field set, independently.
int effectiveVersion = version;
if (version == 0 && versions.Count > 1)
effectiveVersion = schemaVersion >= 0 ? schemaVersion : versions[versions.Count - 1];

var fieldsForVersion = GetFieldsForVersion(messageDto.Fields, effectiveVersion, sourceContext, context);

string headerTypeName = "MessageHeader";
if (context.GeneratedTypeNames.TryGetValue(context.HeaderType, out var resolvedHeaderName))
Expand All @@ -42,17 +52,17 @@ internal class MessagesCodeGenerator : ICodeGenerator
ns,
generatedMessageName,
messageDto.Id,
$"{messageDto.Description} (Version {version})",
$"{messageDto.Description} (Version {effectiveVersion})",
messageDto.SemanticType,
messageDto.Deprecated,
fieldsForVersion,
BuildConstants(messageDto.Constants, context),
BuildGroups(messageDto.Groups, versionNamespace, context, sourceContext),
GetDataForVersion(messageDto.Data, version, context),
GetDataForVersion(messageDto.Data, effectiveVersion, context),
messageDto.BlockLength,
context.EndianConversion,
schema.Id,
version.ToString(),
effectiveVersion.ToString(),
headerTypeName
);
int estimatedCapacity = 2048 + fieldsForVersion.Count * 256
Expand All @@ -66,7 +76,7 @@ internal class MessagesCodeGenerator : ICodeGenerator
yield return (fileName, sb.ToString());

// Compute this version's wire BLOCK_LENGTH using the version's *own* field set
// (sinceVersion <= version), NOT the V0 effectiveVersion override. This is what
// (sinceVersion <= version), NOT the widened effectiveVersion above. This is what
// a producer at that version would have written on the wire — the basis for the version map.
int blockLength;
if (!string.IsNullOrEmpty(messageDto.BlockLength)
Expand Down
2 changes: 1 addition & 1 deletion src/SbeCodeGenerator/SbeSourceGenerator.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<IncludeBuildOutput>false</IncludeBuildOutput>
<PackageId>SbeSourceGenerator</PackageId>
<Title>SBE Source Generator</Title>
<Version>1.2.0</Version>
<Version>1.2.1</Version>
<Authors>Pedro Sakuma</Authors>
<Company>Pedro Sakuma</Company>
<Product>SBE Source Generator</Product>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,27 @@ public void SinceVersion_GeneratesSeparateTypesForEachVersion()
}

[Fact]
public void V0Type_HasOnlyBaseFields()
public void V0Type_IsWidestStruct_IncludesAllSinceVersionFields()
{
// V0 should have only orderId and price (16 bytes)
Assert.Equal(16, V0.EvolvingOrderData.MESSAGE_SIZE);

// Verify we can create and use V0 type
// Issue #143/#149: the unsuffixed (V0-namespace) struct is the widest variant —
// it includes every field added across the schema's version history (orderId + price
// + quantity + side + slack = 25 bytes), so consumers can read everything a current
// producer would have written. The narrow V0 layout is still available via the
// {Msg}VersionMap + V1/V2 namespaces.
Assert.Equal(25, V0.EvolvingOrderData.MESSAGE_SIZE);

Span<byte> buffer = stackalloc byte[V0.EvolvingOrderData.MESSAGE_SIZE];
ref V0.EvolvingOrderData message = ref MemoryMarshal.AsRef<V0.EvolvingOrderData>(buffer);

message.OrderId = 100;
message.Price = 9950;

message.Quantity = 50;
message.Side = 1;

Assert.Equal(100, message.OrderId.Value);
Assert.Equal(9950, message.Price.Value);
Assert.Equal(50, message.Quantity);
Assert.Equal(1, message.Side);
}

[Fact]
Expand Down
Loading