WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content

Commit 9471a2e

Browse files
committed
align xlang struct serialization
1 parent 2c86a77 commit 9471a2e

File tree

23 files changed

+545
-251
lines changed

23 files changed

+545
-251
lines changed

cpp/fory/serialization/struct_serializer.h

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -318,12 +318,12 @@ template <typename T> struct CompileTimeFieldHelpers {
318318
}
319319
}
320320

321-
/// Returns true if the field at Index is nullable.
321+
/// Returns true if the field at Index is nullable for fingerprint computation.
322322
/// This checks:
323323
/// 1. If the field is a fory::field<>, use its is_nullable metadata
324324
/// 2. Else if FORY_FIELD_TAGS is defined, use that metadata
325-
/// 3. Otherwise, use legacy behavior: requires_ref_metadata_v (optional,
326-
/// shared_ptr, unique_ptr are all nullable)
325+
/// 3. Otherwise, use xlang defaults: only std::optional is nullable
326+
/// (For xlang: nullable=false by default, except for Optional types)
327327
template <size_t Index> static constexpr bool field_nullable() {
328328
if constexpr (FieldCount == 0) {
329329
return false;
@@ -339,11 +339,12 @@ template <typename T> struct CompileTimeFieldHelpers {
339339
else if constexpr (::fory::detail::has_field_tags_v<T>) {
340340
return ::fory::detail::GetFieldTagEntry<T, Index>::is_nullable;
341341
}
342-
// For non-wrapped types, use legacy behavior:
343-
// optional, shared_ptr, unique_ptr are all "nullable" in terms of
344-
// wire format (they write ref/null flags)
342+
// For non-wrapped types, use xlang defaults:
343+
// Only std::optional is nullable (field_is_nullable_v returns true for
344+
// optional). For xlang consistency, shared_ptr/unique_ptr are NOT
345+
// nullable by default - users must explicitly mark them as nullable.
345346
else {
346-
return requires_ref_metadata_v<RawFieldType>;
347+
return field_is_nullable_v<RawFieldType>;
347348
}
348349
}
349350
}

go/fory/pointer.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,26 @@ func (s *ptrToValueSerializer) Write(ctx *WriteContext, refMode RefMode, writeTy
6060
return
6161
}
6262
ctx.Buffer().WriteInt8(NotNullValueFlag)
63+
case RefModeNone:
64+
// For RefModeNone with nullable=false, we should NOT write any null flag.
65+
// The xlang protocol expects the value data directly without any header.
66+
// If the pointer is nil, write a zero/default value for the underlying type.
67+
// This can happen in schema evolution when a field is missing from the remote data.
68+
if value.IsNil() {
69+
// Create a zero value for the underlying type and write it
70+
zeroValue := reflect.New(value.Type().Elem()).Elem()
71+
if writeType {
72+
typeInfo, err := ctx.TypeResolver().getTypeInfo(zeroValue, true)
73+
if err != nil {
74+
ctx.SetError(FromError(err))
75+
return
76+
}
77+
ctx.TypeResolver().WriteTypeInfo(ctx.Buffer(), typeInfo, ctx.Err())
78+
}
79+
s.valueSerializer.WriteData(ctx, zeroValue)
80+
return
81+
}
82+
// Do NOT write any flag - just continue to write the value data
6383
}
6484
if writeType {
6585
// Always use TypeResolver to get the correct TypeID from registered TypeInfo
@@ -116,6 +136,9 @@ func (s *ptrToValueSerializer) Read(ctx *ReadContext, refMode RefMode, readType
116136
if flag == NullFlag {
117137
return
118138
}
139+
case RefModeNone:
140+
// For RefModeNone with nullable=false, no null flag was written.
141+
// Just continue to read the value data directly.
119142
}
120143
if readType {
121144
// Read type info - in compatible mode this contains the serializer with fieldDefs

go/fory/skip.go

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -673,17 +673,8 @@ func skipValue(ctx *ReadContext, fieldDef FieldDef, readRefFlag bool, isField bo
673673
}
674674

675675
// fieldNeedWriteRef determines if a field needs a ref flag based on its type and nullability
676+
// For xlang: only nullable fields need ref/null header. Non-nullable fields are written directly.
677+
// This matches the xlang spec where nullable=false means no null flag is written.
676678
func fieldNeedWriteRef(typeId TypeId, nullable bool) bool {
677-
if nullable {
678-
return true
679-
}
680-
// Non-nullable container types still need ref tracking
681-
switch typeId {
682-
case LIST, SET, MAP:
683-
return true
684-
case STRING, BINARY:
685-
return true
686-
default:
687-
return false
688-
}
679+
return nullable
689680
}

0 commit comments

Comments
 (0)