Skip to content

Commit 537ebe6

Browse files
author
Simon Beyzerov
committed
add preliminary unset field semantics (disc #536)
annotate Struct fields as optional and Structs as strict add validation logic add preliminary testing Signed-off-by: Simon Beyzerov <[email protected]>
1 parent ce58f98 commit 537ebe6

File tree

13 files changed

+551
-44
lines changed

13 files changed

+551
-44
lines changed

cpp/csp/adapters/kafka/KafkaInputAdapter.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,8 @@ void KafkaInputAdapter::processMessage( RdKafka::Message* message, bool live, cs
113113
if( m_tickTimestampField )
114114
msgTime = m_tickTimestampField->value<DateTime>(tick.get());
115115

116+
tick.get() -> validate();
117+
116118
bool pushLive = shouldPushLive(live, msgTime);
117119
if( shouldProcessMessage( pushLive, msgTime ) )
118120
pushTick(pushLive, msgTime, std::move(tick), batch);

cpp/csp/adapters/parquet/ParquetReaderColumnAdapter.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,7 @@ void ParquetStructAdapter::dispatchValue( const utils::Symbol *symbol, bool isNu
520520
{
521521
fieldSetter( s );
522522
}
523+
s -> validate();
523524
dispatchedValue = &s;
524525
}
525526

cpp/csp/adapters/utils/JSONMessageStructConverter.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,8 @@ StructPtr JSONMessageStructConverter::convertJSON( const char * fieldname, const
145145
} );
146146
}
147147

148+
struct_ -> validate();
149+
148150
return struct_;
149151
}
150152

@@ -251,6 +253,7 @@ csp::StructPtr JSONMessageStructConverter::asStruct( void * bytes, size_t size )
251253
}
252254
);
253255
}
256+
// root struct validation (validate()) deferred to adapter level
254257

255258
return data;
256259
}

cpp/csp/adapters/websocket/ClientInputAdapter.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ void ClientInputAdapter::processMessage( void* c, size_t t, PushBatch* batch )
3131
if( dataType() -> type() == CspType::Type::STRUCT )
3232
{
3333
auto tick = m_converter -> asStruct( c, t );
34+
tick.get() -> validate();
3435
pushTick( std::move(tick), batch );
3536
} else if ( dataType() -> type() == CspType::Type::STRING )
3637
{

cpp/csp/cppnodes/baselibimpl.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -705,6 +705,7 @@ DECLARE_CPPNODE( struct_fromts )
705705
);
706706
}
707707

708+
out.get() -> validate( );
708709
CSP_OUTPUT( std::move( out ) );
709710
}
710711

@@ -758,7 +759,7 @@ DECLARE_CPPNODE( struct_collectts )
758759
}
759760
);
760761
}
761-
762+
out.get() -> validate( );
762763
CSP_OUTPUT( std::move( out ) );
763764
}
764765

cpp/csp/engine/Struct.cpp

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,23 @@
11
#include <csp/core/System.h>
22
#include <csp/engine/Struct.h>
33
#include <algorithm>
4+
#include <ranges>
5+
#include <string>
46

57
namespace csp
68
{
79

810
StructField::StructField( CspTypePtr type, const std::string & fieldname,
9-
size_t size, size_t alignment ) :
11+
size_t size, size_t alignment, bool isOptional ) :
1012
m_fieldname( fieldname ),
1113
m_offset( 0 ),
1214
m_size( size ),
1315
m_alignment( alignment ),
1416
m_maskOffset( 0 ),
1517
m_maskBit( 0 ),
1618
m_maskBitMask( 0 ),
17-
m_type( type )
19+
m_type( type ),
20+
m_isOptional( isOptional )
1821
{
1922
}
2023

@@ -33,8 +36,8 @@ and adjustments required for the hidden fields
3336
3437
*/
3538

36-
StructMeta::StructMeta( const std::string & name, const Fields & fields,
37-
std::shared_ptr<StructMeta> base ) : m_name( name ), m_base( base ), m_fields( fields ),
39+
StructMeta::StructMeta( const std::string & name, const Fields & fields, bool isStrict,
40+
std::shared_ptr<StructMeta> base ) : m_name( name ), m_base( base ), m_isStrict( isStrict ), m_fields( fields ),
3841
m_size( 0 ), m_partialSize( 0 ), m_partialStart( 0 ), m_nativeStart( 0 ), m_basePadding( 0 ),
3942
m_maskLoc( 0 ), m_maskSize( 0 ), m_firstPartialField( 0 ), m_firstNativePartialField( 0 ),
4043
m_isPartialNative( true ), m_isFullyNative( true )
@@ -494,6 +497,43 @@ void StructMeta::destroy( Struct * s ) const
494497
m_base -> destroy( s );
495498
}
496499

500+
void StructMeta::validate( const Struct * s ) const
501+
{
502+
bool encountered_non_strict = false;
503+
504+
for( const StructMeta * cur = this; cur; cur = cur -> m_base.get() )
505+
{
506+
encountered_non_strict |= !cur -> isStrict();
507+
if( !cur -> isStrict() ) {
508+
continue;
509+
}
510+
511+
// rule 1: a non-strict struct may not inherit (directly or indirectly) from a strict base
512+
if (encountered_non_strict)
513+
CSP_THROW( ValueError, "Struct '" << s -> meta() -> name() << "' has non-strict inheritance of strict base '" << cur -> name() << "'" );
514+
515+
// rule 2: all local fields are set
516+
std::string missing_fields;
517+
for(const auto& field : cur->m_fields) {
518+
if(!field->isSet(s)) {
519+
if(missing_fields.empty()) {
520+
missing_fields = field->fieldname();
521+
} else {
522+
missing_fields += ", " + field->fieldname();
523+
}
524+
}
525+
}
526+
527+
// raise error if any fields are missing
528+
if (!missing_fields.empty()) {
529+
CSP_THROW(ValueError,
530+
"Strict struct '" << cur->name() << "' missing required fields: " << missing_fields);
531+
}
532+
}
533+
}
534+
535+
536+
497537
Struct::Struct( const std::shared_ptr<const StructMeta> & meta )
498538
{
499539
//Initialize meta shared_ptr

cpp/csp/engine/Struct.h

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ class StructField
3535

3636
bool isNative() const { return m_type -> type() <= CspType::Type::MAX_NATIVE_TYPE; }
3737

38+
bool isOptional() const { return m_isOptional; }
39+
3840
void setOffset( size_t off ) { m_offset = off; }
3941
void setMaskOffset( size_t off, uint8_t bit )
4042
{
@@ -75,7 +77,7 @@ class StructField
7577
protected:
7678

7779
StructField( CspTypePtr type, const std::string & fieldname,
78-
size_t size, size_t alignment );
80+
size_t size, size_t alignment, bool isOptional );
7981

8082
void setIsSet( Struct * s ) const
8183
{
@@ -108,6 +110,7 @@ class StructField
108110
uint8_t m_maskBit;
109111
uint8_t m_maskBitMask;
110112
CspTypePtr m_type;
113+
const bool m_isOptional;
111114
};
112115

113116
using StructFieldPtr = std::shared_ptr<StructField>;
@@ -120,7 +123,7 @@ class NativeStructField : public StructField
120123

121124
public:
122125
NativeStructField() {}
123-
NativeStructField( const std::string & fieldname ) : NativeStructField( CspType::fromCType<T>::type(), fieldname )
126+
NativeStructField( const std::string & fieldname, bool isOptional ) : NativeStructField( CspType::fromCType<T>::type(), fieldname, isOptional )
124127
{
125128
}
126129

@@ -157,7 +160,7 @@ class NativeStructField : public StructField
157160
}
158161

159162
protected:
160-
NativeStructField( CspTypePtr type, const std::string & fieldname ) : StructField( type, fieldname, sizeof( T ), alignof( T ) )
163+
NativeStructField( CspTypePtr type, const std::string & fieldname, bool isOptional ) : StructField( type, fieldname, sizeof( T ), alignof( T ), isOptional )
161164
{}
162165
};
163166

@@ -179,7 +182,8 @@ using TimeStructField = NativeStructField<Time>;
179182
class CspEnumStructField final : public NativeStructField<CspEnum>
180183
{
181184
public:
182-
CspEnumStructField( CspTypePtr type, const std::string & fieldname ) : NativeStructField( type, fieldname )
185+
CspEnumStructField( CspTypePtr type, const std::string & fieldname, bool isOptional ) : NativeStructField( type,
186+
fieldname, isOptional )
183187
{}
184188
};
185189

@@ -218,8 +222,8 @@ class NotImplementedStructField : public StructField
218222
class NonNativeStructField : public StructField
219223
{
220224
public:
221-
NonNativeStructField( CspTypePtr type, const std::string &fieldname, size_t size, size_t alignment ) :
222-
StructField( type, fieldname, size, alignment )
225+
NonNativeStructField( CspTypePtr type, const std::string &fieldname, size_t size, size_t alignment, bool isOptional ) :
226+
StructField( type, fieldname, size, alignment, isOptional )
223227
{}
224228

225229
virtual void initialize( Struct * s ) const = 0;
@@ -244,8 +248,8 @@ class StringStructField final : public NonNativeStructField
244248
public:
245249
using CType = csp::CspType::StringCType;
246250

247-
StringStructField( CspTypePtr type, const std::string & fieldname ) :
248-
NonNativeStructField( type, fieldname, sizeof( CType ), alignof( CType ) )
251+
StringStructField( CspTypePtr type, const std::string & fieldname, bool isOptional ) :
252+
NonNativeStructField( type, fieldname, sizeof( CType ), alignof( CType ), isOptional )
249253
{}
250254

251255
void initialize( Struct * s ) const override
@@ -343,8 +347,8 @@ class ArrayStructField : public NonNativeStructField
343347
}
344348

345349
public:
346-
ArrayStructField( CspTypePtr arrayType, const std::string & fieldname ) :
347-
NonNativeStructField( arrayType, fieldname, sizeof( CType ), alignof( CType ) )
350+
ArrayStructField( CspTypePtr arrayType, const std::string & fieldname, bool isOptional ) :
351+
NonNativeStructField( arrayType, fieldname, sizeof( CType ), alignof( CType ), isOptional )
348352
{}
349353

350354
const CType & value( const Struct * s ) const
@@ -421,8 +425,8 @@ class ArrayStructField : public NonNativeStructField
421425
class DialectGenericStructField : public NonNativeStructField
422426
{
423427
public:
424-
DialectGenericStructField( const std::string & fieldname, size_t size, size_t alignment ) :
425-
NonNativeStructField( CspType::DIALECT_GENERIC(), fieldname, size, alignment )
428+
DialectGenericStructField( const std::string & fieldname, size_t size, size_t alignment, bool isOptional ) :
429+
NonNativeStructField( CspType::DIALECT_GENERIC(), fieldname, size, alignment, isOptional )
426430
{}
427431

428432
const DialectGenericType & value( const Struct * s ) const
@@ -587,21 +591,24 @@ class StructMeta : public std::enable_shared_from_this<StructMeta>
587591
using FieldNames = std::vector<std::string>;
588592

589593
//Fields will be re-arranged and assigned their offsets in StructMeta for optimal performance
590-
StructMeta( const std::string & name, const Fields & fields, std::shared_ptr<StructMeta> base = nullptr );
594+
StructMeta( const std::string & name, const Fields & fields, bool isStrict, std::shared_ptr<StructMeta> base = nullptr );
591595
virtual ~StructMeta();
592596

593597
const std::string & name() const { return m_name; }
594598
size_t size() const { return m_size; }
595599
size_t partialSize() const { return m_partialSize; }
596600

597601
bool isNative() const { return m_isFullyNative; }
602+
bool isStrict() const { return m_isStrict; }
598603

599604
const Fields & fields() const { return m_fields; }
600605
const FieldNames & fieldNames() const { return m_fieldnames; }
601606

602607
size_t maskLoc() const { return m_maskLoc; }
603608
size_t maskSize() const { return m_maskSize; }
604609

610+
void validate( const Struct * s ) const;
611+
605612
const StructFieldPtr & field( const char * name ) const
606613
{
607614
static StructFieldPtr s_empty;
@@ -652,7 +659,8 @@ class StructMeta : public std::enable_shared_from_this<StructMeta>
652659
std::shared_ptr<StructMeta> m_base;
653660
StructPtr m_default;
654661
FieldMap m_fieldMap;
655-
662+
bool m_isStrict;
663+
656664
//fields in order, memory owners of field objects which in turn own the key memory
657665
//m_fields includes all base fields as well. m_fieldnames maintains the proper iteration order of fields
658666
Fields m_fields;
@@ -738,6 +746,11 @@ class Struct
738746
return meta() -> allFieldsSet( this );
739747
}
740748

749+
void validate() const
750+
{
751+
meta() -> validate( this );
752+
}
753+
741754

742755
//used to cache dialect representations of this struct, if needed
743756
void * dialectPtr() const { return hidden() -> dialectPtr; }
@@ -822,8 +835,8 @@ bool TypedStructPtr<T>::operator==( const TypedStructPtr<T> & rhs ) const
822835
class StructStructField final : public NonNativeStructField
823836
{
824837
public:
825-
StructStructField( CspTypePtr cspType, const std::string &fieldname ) :
826-
NonNativeStructField( cspType, fieldname, sizeof( StructPtr ), alignof( StructPtr ) )
838+
StructStructField( CspTypePtr cspType, const std::string &fieldname, bool isOptional ) :
839+
NonNativeStructField( cspType, fieldname, sizeof( StructPtr ), alignof( StructPtr ), isOptional )
827840
{
828841
CSP_ASSERT( cspType -> type() == CspType::Type::STRUCT );
829842
m_meta = std::static_pointer_cast<const CspStructType>( cspType ) -> meta();

0 commit comments

Comments
 (0)