@@ -39,47 +39,106 @@ and adjustments required for the hidden fields
3939
4040*/
4141
42- StructMeta::StructMeta ( const std::string & name, const Fields & fields, bool isStrict,
43- std::shared_ptr<StructMeta> base ) : m_name( name ), m_base( base ), m_fields( fields ), m_optionalFieldsBitMasks(),
42+ StructMeta::StructMeta ( const std::string & name, Fields fields, bool isStrict,
43+ std::shared_ptr<StructMeta> base ) : m_name( name ), m_base( base ), m_fields(), m_optionalFieldsBitMasks(),
4444 m_size ( 0 ), m_partialSize( 0 ), m_partialStart( 0 ), m_nativeStart( 0 ), m_basePadding( 0 ),
4545 m_maskLoc( 0 ), m_maskSize( 0 ), m_firstPartialField( 0 ), m_firstNativePartialField( 0 ),
4646 m_isPartialNative( true ), m_isFullyNative( true ), m_isStrict( isStrict )
4747{
48- if ( m_fields .empty () && !m_base )
48+ if ( fields .empty () && !m_base )
4949 CSP_THROW ( TypeError, " Struct types must define at least 1 field" );
5050
5151 // sort by sizes, biggest first, to get proper alignment
5252 // group generic objects separately at the start so that we can safely memcpy native types
5353 // decided to place them at the start cause they are most likely size of ptr or greater
5454
55- m_fieldnames.reserve ( m_fields.size () );
56- for ( size_t i = 0 ; i < m_fields.size (); i++ )
57- m_fieldnames.emplace_back ( m_fields[i] -> fieldname () );
55+ std::vector<std::string> fieldnames; // need to keep the original order of the derived struct
56+ fieldnames.reserve ( fields.size () );
57+ for ( auto & f : fields )
58+ fieldnames.emplace_back ( f -> fieldname () );
5859
59- std::sort ( m_fields .begin (), m_fields .end (), []( auto && a, auto && b )
60+ std::sort ( fields .begin (), fields .end (), []( auto && a, auto && b )
6061 {
6162 return a -> isNative () < b -> isNative () ||
6263 a -> size () > b -> size ();
6364 } );
6465
66+ if ( m_base )
67+ {
68+ // The complete inheritance hierarchy must agree on strict/non-strict
69+ if ( m_isStrict != m_base -> isStrict () )
70+ {
71+ CSP_THROW ( ValueError,
72+ " Struct " << m_name << " was declared " << ( m_isStrict ? " strict" : " non-strict" ) << " but derives from "
73+ << m_base -> name () << " which is " << ( m_base -> isStrict () ? " strict" : " non-strict" )
74+ );
75+ }
76+
77+ m_fields = m_base -> m_fields;
78+ m_fieldnames = m_base -> m_fieldnames;
79+ m_fieldMap = m_base -> m_fieldMap;
80+ m_firstPartialField = m_base -> m_fields.size ();
81+
82+ // append non-override derived fieldnames in their original declaration order
83+ for ( auto & fn : fieldnames )
84+ {
85+ if ( m_fieldMap.find ( fn.c_str () ) == m_fieldMap.end () )
86+ m_fieldnames.push_back ( std::move ( fn ) );
87+ }
88+ }
89+ else
90+ m_fieldnames = std::move ( fieldnames );
91+
92+ std::vector<StructFieldPtr> derivedFields; // easier to store this for pre-processing and only append derived fields to m_fields at the end
93+ for ( auto & derivedField : fields )
94+ {
95+ auto rv = m_fieldMap.emplace ( derivedField -> fieldname ().c_str (), derivedField );
96+ if ( !rv.second )
97+ {
98+ auto derivedTypeEnum = derivedField -> type () -> type ();
99+ auto baseTypeEnum = rv.first -> second -> type () -> type ();
100+
101+ // only allowed to override fields from a base class if types are DIALECT_GENERIC or STRUCT
102+ // beyond that, its up to the dialect-specific struct impl (i.e. PyStruct) to decide whether the override types are valid
103+ if ( derivedTypeEnum != baseTypeEnum || ( derivedTypeEnum != CspType::Type::DIALECT_GENERIC && derivedTypeEnum != CspType::Type::STRUCT ) )
104+ CSP_THROW ( ValueError, " csp Struct " << name << " attempted to add existing field " << derivedField -> fieldname () <<
105+ " which is not allowed: base field type " << baseTypeEnum << " and derived field type " << derivedTypeEnum );
106+
107+ // replace the base field in-place with the derived override,
108+ for ( size_t baseIdx = 0 ; baseIdx < m_firstPartialField; ++baseIdx )
109+ {
110+ if ( m_fields[ baseIdx ] -> fieldname () == derivedField -> fieldname () )
111+ {
112+ derivedField -> setOffset ( m_fields[ baseIdx ] -> offset () );
113+ derivedField -> setMaskOffset ( m_fields[ baseIdx ] -> maskOffset (), m_fields[ baseIdx ] -> maskBit () );
114+ m_fields[ baseIdx ] = derivedField;
115+ break ;
116+ }
117+ }
118+ rv.first -> second = derivedField; // field map
119+ }
120+ else
121+ derivedFields.push_back ( derivedField );
122+ }
123+
65124 size_t baseSize = m_base ? m_base -> size () : 0 ;
66125 size_t offset = baseSize;
67126 m_basePadding = 0 ;
68127
69- // align to first field's alignment
70- if ( m_fields. size () && ( offset % m_fields[ 0 ] -> alignment () != 0 ) )
71- m_basePadding = m_fields[ 0 ] -> alignment () - offset % m_fields[ 0 ] -> alignment ();
128+ // align to first field's alignment if there are derived fields remaining
129+ if ( !derivedFields. empty () && ( offset % derivedFields[ 0 ] -> alignment () != 0 ) )
130+ m_basePadding = derivedFields[ 0 ] -> alignment () - offset % derivedFields[ 0 ] -> alignment ();
72131
73132 offset += m_basePadding;
74- if ( !m_fields .empty () )
75- CSP_ASSERT ( ( offset % m_fields[ 0 ] -> alignment () ) == 0 );
133+ if ( !derivedFields .empty () )
134+ CSP_ASSERT ( ( offset % derivedFields[ 0 ] -> alignment () ) == 0 );
76135
77136 m_partialStart = offset;
78137 m_nativeStart = m_partialStart;
138+ m_firstNativePartialField = m_firstPartialField;
79139
80- for ( size_t idx = 0 ; idx < m_fields. size (); ++idx )
140+ for ( auto & f : derivedFields )
81141 {
82- auto & f = m_fields[ idx ];
83142 if ( offset % f -> alignment () != 0 )
84143 offset += f -> alignment () - offset % f -> alignment ();
85144
@@ -92,24 +151,24 @@ StructMeta::StructMeta( const std::string & name, const Fields & fields, bool is
92151 if ( !f -> isNative () )
93152 {
94153 m_nativeStart = offset;
95- m_firstNativePartialField = idx + 1 ;
154+ m_firstNativePartialField += 1 ;
96155 }
97156 }
98157
99158 m_isFullyNative = m_isPartialNative && ( m_base ? m_base -> isNative () : true );
100159
101- // Setup masking bits for our fields
102- // NOTE we can be more efficient by sticking masks into any potential alignment gaps, dont want to spend time on it
103- // at this point
160+ // setup masking bits for the remaining derived (partial) fields
161+ // note that this is done after the override dedup above so that overridden fields don't consume mask bits.
162+ // NOTE we can be more efficient by sticking masks into any potential alignment gaps, dont want to spend time on it
163+ size_t optionalFieldCount = std::count_if ( derivedFields.begin (), derivedFields.end (), []( const auto & f ) { return f -> isOptional (); } );
164+ size_t partialFieldCount = derivedFields.size ();
104165
105- size_t optionalFieldCount = std::count_if ( m_fields.begin (), m_fields.end (), []( const auto & f ) { return f -> isOptional (); } );
106-
107- m_maskSize = !m_fields.empty () ? 1 + ( ( m_fields.size () + optionalFieldCount - 1 ) / 8 ) : 0 ;
166+ m_maskSize = partialFieldCount > 0 ? 1 + ( ( partialFieldCount + optionalFieldCount - 1 ) / 8 ) : 0 ;
108167 m_size = offset + m_maskSize;
109168 m_partialSize = m_size - baseSize;
110169 m_maskLoc = m_size - m_maskSize;
111-
112- uint8_t numRemainingBits = ( m_fields. size () + optionalFieldCount ) % 8 ;
170+
171+ uint8_t numRemainingBits = ( partialFieldCount + optionalFieldCount ) % 8 ;
113172 m_lastByteMask = ( 1u << numRemainingBits ) - 1 ;
114173
115174 size_t maskLoc = m_maskLoc;
@@ -118,9 +177,8 @@ StructMeta::StructMeta( const std::string & name, const Fields & fields, bool is
118177 // Set optional fields first so that their 2-bits never cross a byte boundary
119178 // Put both the set bits and none bits in the same vector to avoid fragmentation
120179 m_optionalFieldsBitMasks.resize ( 2 * m_maskSize );
121- for ( size_t i = 0 ; i < m_fields. size (); ++i )
180+ for ( auto & f : derivedFields )
122181 {
123- auto & f = m_fields[ i ];
124182 if ( f -> isOptional () )
125183 {
126184 f -> setMaskOffset ( maskLoc, maskBit );
@@ -134,9 +192,8 @@ StructMeta::StructMeta( const std::string & name, const Fields & fields, bool is
134192 }
135193 }
136194
137- for ( size_t i = 0 ; i < m_fields. size (); ++i )
195+ for ( auto & f : derivedFields )
138196 {
139- auto & f = m_fields[ i ];
140197 if ( !f -> isOptional () )
141198 {
142199 f -> setMaskOffset ( maskLoc, maskBit );
@@ -148,31 +205,8 @@ StructMeta::StructMeta( const std::string & name, const Fields & fields, bool is
148205 }
149206 }
150207
151- if ( m_base )
152- {
153- // The complete inheritance hierarchy must agree on strict/non-strict
154- if ( m_isStrict != m_base -> isStrict () )
155- {
156- CSP_THROW ( ValueError,
157- " Struct " << m_name << " was declared " << ( m_isStrict ? " strict" : " non-strict" ) << " but derives from "
158- << m_base -> name () << " which is " << ( m_base -> isStrict () ? " strict" : " non-strict" )
159- );
160- }
161-
162- m_fields.insert ( m_fields.begin (), m_base -> m_fields.begin (), m_base -> m_fields.end () );
163- m_fieldnames.insert ( m_fieldnames.begin (), m_base -> m_fieldnames.begin (), m_base -> m_fieldnames.end () );
164-
165- m_firstPartialField = m_base -> m_fields.size ();
166- m_firstNativePartialField += m_base -> m_fields.size ();
167- m_fieldMap = m_base -> m_fieldMap;
168- }
169-
170- for ( size_t idx = m_firstPartialField; idx < m_fields.size (); ++idx )
171- {
172- auto rv = m_fieldMap.emplace ( m_fields[ idx ] -> fieldname ().c_str (), m_fields[ idx ] );
173- if ( !rv.second )
174- CSP_THROW ( ValueError, " csp Struct " << name << " attempted to add existing field " << m_fields[ idx ] -> fieldname () );
175- }
208+ // append derived fields to m_fields after all processing is done
209+ m_fields.insert ( m_fields.end (), derivedFields.begin (), derivedFields.end () );
176210}
177211
178212StructMeta::~StructMeta ()
0 commit comments