meta/serialize.pm (870 lines of code) (raw):

#!/usr/bin/perl # # Copyright (c) 2014 Microsoft Open Technologies, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 # # THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR # CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT # LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS # FOR A PARTICULAR PURPOSE, MERCHANTABILITY OR NON-INFRINGEMENT. # # See the Apache Version 2.0 License for specific language governing # permissions and limitations under the License. # # Microsoft would like to thank the following companies for their review and # assistance with these files: Intel Corporation, Mellanox Technologies Ltd, # Dell Products, L.P., Facebook, Inc., Marvell International Ltd. # # @file serialize.pm # # @brief This module defines SAI Metadata Serialize Parser # package serialize; use strict; use warnings; use diagnostics; use Data::Dumper; use utils; use xmlutils; require Exporter; sub CreateSerializeForEnums { WriteSectionComment "Enum serialize methods"; for my $key (sort keys %main::SAI_ENUMS) { next if $key =~ /_attr_t$/; if (not $key =~ /^sai_(\w+)_t$/) { LogWarning "wrong enum name '$key'"; next; } my $suffix = $1; WriteHeader "extern int sai_serialize_$suffix("; WriteHeader "_Out_ char *buffer,"; WriteHeader "_In_ $key $suffix);\n"; WriteSource "int sai_serialize_$suffix("; WriteSource "_Out_ char *buffer,"; WriteSource "_In_ $key $suffix)"; WriteSource "{"; WriteSource "return sai_serialize_enum(buffer, &sai_metadata_enum_$key, $suffix);"; WriteSource "}"; } } # # we are using sprintf here, but in all cases it's const string passed, so # compiler will optimize this to strcpy, and no snprintf function will be # actually called, actual functions called will be those written by user in # saiserialize.c and optimization should focus on those functions # # TODO we need version with like snprintf to write only N characters since # right how we don't know how long output will be, for long arrays it can be # even kB # # we will treat notification params as struct members and they will be # serialized as json object all consts printfs could be exchanged to memcpy for # optimize but we assume number of notifications is small, and this is fast # enough # sub CreateSerializeSingleStruct { my $structName = shift; my %structInfoEx = ExtractStructInfoEx($structName, "struct_"); ProcessMembersForSerialize(\%structInfoEx); } sub IsMetadataStruct { # # check if structure is declared inside metadata # my $refStructInfoEx = shift; my $key = $refStructInfoEx->{keys}[0]; return 1 if $refStructInfoEx->{membersHash}{$key}->{file} =~ m!(meta/sai\w+.h|^sai(meta\w+.h))$!; return 0; } # TODO for lists we need countOnly param, as separate version # @param[in] only_count Flag specifies whether on *_list_t only # list count should be serialized, this is handy when serializing # attributes when API returned #SAI_STATUS_BUFFER_OVERFLOW. # TODO on s32/s32_list in struct we could declare enum type sub EmitSerializeFunctionHeader { my $refStructInfoEx = shift; my %structInfoEx = %{ $refStructInfoEx }; my $structName = $structInfoEx{name}; my $structBase = $structInfoEx{baseName}; my $membersHash = $structInfoEx{membersHash}; my @keys = @{ $structInfoEx{keys} }; WriteHeader "extern int sai_serialize_$structBase("; WriteHeader "_Out_ char *buf,"; WriteSource "int sai_serialize_$structBase("; WriteSource "_Out_ char *buf,"; if (defined $structInfoEx{union} and not defined $structInfoEx{extraparam}) { LogError "union $structName, extraparam required"; return; } if (defined $structInfoEx{ismethod}) { # # we create serialize method as this funcion was method instead of # struct, this will be used to create serialize for notifications # for my $name (@keys) { my $type = $membersHash->{$name}{type}; LogDebug "$structName $structBase $name $type"; my $last = 1 if $keys[-1] eq $name; my $end = (defined $last) ? ")" : ","; my $endheader = (defined $last) ? ");" : ","; WriteSource "_In_ $type $name$end"; WriteHeader "_In_ $type $name$endheader"; } } else { if (defined $structInfoEx{extraparam}) { my @params = @{ $structInfoEx{extraparam} }; for my $param (@params) { WriteHeader "_In_ $param,"; WriteSource "_In_ $param,"; } } WriteHeader "_In_ const $structName *$structBase);\n"; WriteSource "_In_ const $structName *$structBase)"; } } sub GetTypeInfoForSerialize { my ($refStructInfoEx, $name) = @_; my %structInfoEx = %{ $refStructInfoEx }; my $structName = $structInfoEx{name}; my $structBase = $structInfoEx{baseName}; my $type = $structInfoEx{membersHash}->{$name}{type}; my %TypeInfo = ( name => $name, type => $type, noptrtype => $type, needQuote => 0, ispointer => 0, isattribute => 0, amp => "", deamp => "", objectType =>"UNKNOWN_OBJ_TYPE", castName => ""); $type = $1 if $type =~ /^const\s+(.+)$/; if ($type =~ /^(sai_\w+_t)\[(\d+)\]$/) { $type = $1; $TypeInfo{constCount} = $2; $TypeInfo{ispointer} = 1; $TypeInfo{deamp} = "&"; $TypeInfo{noptrtype} = $1; } if ($type =~ /(.*)\s*\*$/) { $type =~ s!\s*\*$!!; $TypeInfo{ispointer} = 1; $TypeInfo{noptrtype} = $type; } $TypeInfo{suffix} = ($type =~ /sai_(\w+)_t/) ? $1 : $type; if ($type =~ /^(bool|sai_size_t)$/) { # ok $TypeInfo{deamp} = "&"; } elsif ($type eq "void") { # treat void* as uint8_t* $TypeInfo{suffix} = "uint8"; $TypeInfo{castName} = "(const uint8_t*)"; } elsif ($type =~ /^sai_ip6_t$/) { $TypeInfo{needQuote} = 1; } elsif ($type =~ /^sai_ip4_t$/) { $TypeInfo{needQuote} = 1; $TypeInfo{deamp} = "&"; } elsif ($type =~ /^sai_(vlan_id)_t$/) { $TypeInfo{suffix} = "uint16"; $TypeInfo{deamp} = "&"; } elsif ($type =~ /^sai_(label_id)_t$/) { $TypeInfo{suffix} = "uint32"; $TypeInfo{deamp} = "&"; } elsif ($type =~ /^sai_(stat_id)_t$/) { $TypeInfo{suffix} = "uint32"; $TypeInfo{deamp} = "&"; } elsif ($type =~ /^sai_(cos|queue_index)_t$/) { $TypeInfo{suffix} = "uint8"; $TypeInfo{deamp} = "&"; } elsif ($type =~ /^(?:sai_)?(u?int\d+)_t$/) { # enums! $TypeInfo{suffix} = $1; $TypeInfo{deamp} = "&"; } elsif ($type =~ m/^sai_(ip_address|ip_prefix)_t$/) { $TypeInfo{needQuote} = 1; $TypeInfo{amp} = "&"; $TypeInfo{deamp} = "&"; } # # NOTE: check for objects is not that simple here, since object_id or # object_list can have multiple different object types allowed depending on # attribute which is being serialized and it may be a hudge task to make this # check here and also it would require to pass meta param to each serialized # struct even if meta param is not required # # elsif ($type =~ m/^sai_object_list_t$/) # { # $TypeInfo{amp} = "&"; # # if (not defined $structInfoEx{membersHash}->{$name}{objects}) # { # LogError "param '$name' is '$type' on '$structName' and requires object type specified in \@objects"; # return undef; # } # } # elsif ($type =~ m/^sai_object_id_t$/) # { # # TODO object_list check if object is defined # $TypeInfo{needQuote} = 1; # # if (not defined $structInfoEx{membersHash}->{$name}{objects}) # { # LogError "param '$name' is '$type' on '$structName' and requires object type specified in \@objects"; # return undef; # } # } elsif ($type =~ m/^sai_mac_t$/) { $TypeInfo{needQuote} = 1; } elsif ($type =~ m/^sai_attr_id_t$/) { $TypeInfo{needQuote} = 1; } elsif ($type =~ m/^sai_object_id_t$/) { $TypeInfo{needQuote} = 1; $TypeInfo{deamp} = "&"; } elsif ($type =~ /^sai_pointer_t$/) { # need quote since "ptr:" is added on serialize $TypeInfo{needQuote} = 1; $TypeInfo{deamp} = "&"; } elsif ($type =~ /^sai_attribute_t$/) { $TypeInfo{amp} = "&"; $TypeInfo{deamp} = "&"; $TypeInfo{isattribute} = 1; if (not defined $structInfoEx{membersHash}->{$name}{objects}) { LogError "param '$name' is '$type' on '$structName' and requires object type specified in \@objects"; return undef; } my @ot = @{ $structInfoEx{membersHash}->{$name}{objects} }; if (scalar@ot != 1) { LogWarning "expected only 1 object type, but given '@ot'"; return undef; } $TypeInfo{objectType} = $ot[0]; if (not defined $main::OBJECT_TYPE_MAP{$TypeInfo{objectType}}) { LogError "unknown object type '$TypeInfo{objectType}' on $structName :: $name"; return undef; } } elsif (defined $main::ALL_STRUCTS{$type} and $type =~ /^sai_(\w+)_t$/) { $TypeInfo{amp} = "&"; $TypeInfo{deamp} = "&"; # sai_s32_list_t enum ! } elsif (defined $main::SAI_ENUMS{$type} and $type =~ /^sai_(\w+)_t$/) { $TypeInfo{needQuote} = 1; $TypeInfo{deamp} = "&"; } elsif (defined $main::SAI_UNIONS{$type} and $type =~ /^sai_(\w+)_t$/) { $TypeInfo{union} = 1; $TypeInfo{amp} = "&"; $TypeInfo{deamp} = "&"; } elsif ($type eq "char[32]") { $TypeInfo{needQuote} = 1; $TypeInfo{suffix} = "chardata"; } elsif (defined $main::PRIMITIVE_TYPES{$type} and $main::PRIMITIVE_TYPES{$type}{isarray} == 1) { $TypeInfo{needQuote} = 1; } else { LogError "serialization type not handled $type $name in $structName, FIXME"; return undef; } my $memberName = (defined $structInfoEx{ismethod}) ? $name : "$structBase\->$name"; $memberName = "($TypeInfo{castName}$memberName)" if $TypeInfo{castName} ne ""; $TypeInfo{memberName} = $memberName; $TypeInfo{suffix} = $structInfoEx{membersHash}->{$name}{suffix} if defined $structInfoEx{membersHash}->{$name}{suffix}; return \%TypeInfo; } sub GetCounterNameAndType { my ($refStructInfoEx, $refTypeInfo) = @_; my $refMembersHash = $refStructInfoEx->{membersHash}; my $name = $refTypeInfo->{name}; my $structName = $refStructInfoEx->{name}; my $structBase = $refStructInfoEx->{baseName}; if (defined $refTypeInfo->{constCount}) { my $countMemberName = $refTypeInfo->{constCount}; my $countType = "uint32_t"; return ($countMemberName, $countType, 1); } my $count = $refMembersHash->{$name}{count}; if (not defined $count) { LogError "count must be defined for pointer '$name' in $structName"; return ("undef", "undef"); } if (not defined $refMembersHash->{$count}) { LogWarning "count '$count' not found in '$structName'"; return ("undef", "undef"); } my $countMemberName = $refMembersHash->{$count}{name}; my $countType = $refMembersHash->{$count}{type}; $countMemberName = (defined $refStructInfoEx->{ismethod}) ? $countMemberName: "$structBase\->$countMemberName"; if (not $countType =~ /^(uint32_t|sai_size_t)$/) { LogWarning "count '$count' on '$structName' has invalid type '$countType', expected uint32_t"; return ("undef", "undef"); } if (not defined $refStructInfoEx->{processed}->{$count}) { # check if count was declared before list, since deserialize # needs to know how many items is on list to not make realoc and monitor # number of elements LogError "count member '$count' on $structName is defined after '$name', not allowed"; } return ($countMemberName, $countType); } sub EmitSerializeHeader { WriteSource "{"; WriteSource "char *begin_buf = buf;"; WriteSource "int ret;\n"; WriteSource "EMIT(\"{\");\n"; } sub WriteSkipForMask { # mask is special, since it's combined with field data, but there # is not field corresponded to object or object list so we can skip # them in serialize and deserialize WriteSource "else if (meta->attrvaluetype == SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_OBJECT_ID)"; WriteSource "{"; WriteSource "/* skip */"; WriteSource "}"; WriteSource "else if (meta->attrvaluetype == SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_OBJECT_LIST)"; WriteSource "{"; WriteSource "/* skip */"; WriteSource "}"; } sub EmitSerializeFooter { my $refStructInfoEx = shift; if (defined $refStructInfoEx->{union}) { my $name = $refStructInfoEx->{name}; WriteSkipForMask() if $name eq "sai_acl_field_data_mask_t"; # NOTE: if it's union, we must check if we serialized something # (not always true for acl mask) WriteSource "else"; WriteSource "{"; WriteSource "SAI_META_LOG_WARN(\"nothing was serialized for '$name', bad condition?\");"; # WriteSource "return SAI_SERIALIZE_ERROR;"; WriteSource "return SAI_SERIALIZE_ERROR;" if $name eq "sai_attribute_value_t"; WriteSource "}\n"; } WriteSource "EMIT(\"}\");\n"; WriteSource "return (int)(buf - begin_buf);"; WriteSource "}"; } sub GetEmitMacroName { my $refTypeInfo = shift; return "EMIT_QUOTE_CHECK" if $refTypeInfo->{needQuote}; return "EMIT_CHECK"; } sub GetPassParamsForSerialize { my ($refStructInfoEx, $refTypeInfo) = @_; my $name = $refTypeInfo->{name}; my $refMembersHash = $refStructInfoEx->{membersHash}; return "" if not defined $refMembersHash->{$name}{passparam}; my $structBase = $refStructInfoEx->{baseName}; my @params = @{ $refMembersHash->{$name}{passparam} }; my $passParams = ""; for my $param (@params) { if (not $param =~ /->/ and defined $refMembersHash->{$param}) { $passParams .= "$structBase\->$param, "; } else { $passParams .= "$param, "; } } return $passParams; } sub EmitSerializePrimitive { my ($refStructInfoEx, $refTypeInfo) = @_; my $suffix = $refTypeInfo->{suffix}; my $emitMacro = GetEmitMacroName($refTypeInfo); my $passParams = GetPassParamsForSerialize($refStructInfoEx, $refTypeInfo); my $serializeCall = "sai_serialize_$suffix(buf, $passParams$refTypeInfo->{amp}$refTypeInfo->{memberName})"; WriteSource "$emitMacro($serializeCall, $suffix);"; } sub GetEmitKeyMacroName { my ($refStructInfoEx, $name) = @_; my $firstKey = $refStructInfoEx->{keys}->[0]; return "EMIT_KEY" if ($firstKey eq $name) or defined $refStructInfoEx->{union}; return "EMIT_NEXT_KEY"; } sub EmitSerializeMemberKey { my ($refStructInfoEx, $refTypeInfo) = @_; my $name = $refTypeInfo->{name}; my $emitKeyMacro = GetEmitKeyMacroName($refStructInfoEx, $name); WriteSource "$emitKeyMacro(\"$name\");\n"; } sub GetConditionForSerialize { my ($refStructInfoEx, $refTypeInfo) = @_; my $name = $refTypeInfo->{name}; my $structName = $refStructInfoEx->{name}; my $structBase = $refStructInfoEx->{baseName}; my $refMembersHash = $refStructInfoEx->{membersHash}; my @conditions = @{ $refMembersHash->{ $refTypeInfo->{name} }->{validonly} }; if (scalar @conditions != 1) { LogWarning "only 1 condition is supported for '$name' in '$structName', FIXME"; return ""; } # if condition value is struct member, we need to check # if it was processed previously, since it could be declared # after current member, and then deserialize will fail my $condition = shift @conditions; if (not $condition =~ /^(\w+|\w+->\w+|sai_metadata_\w+\(\w+\)) == (\w+)$/) { LogWarning "invalid condition '$condition' on '$name' in '$structName'"; return ""; } if (defined $refStructInfoEx->{baseName}) { $condition = "(sai_int32_t)$condition" if $refStructInfoEx->{baseName} eq "object_key_entry"; } if (defined $refMembersHash->{$1}) { $condition = "$structBase->$1 == $2"; if (not defined $refStructInfoEx->{processed}->{$1}) { LogError "validonly condition '$1' on $structName is declared after '$name', not allowed"; return ""; } } return $condition; } sub EmitSerializeValidOnlyHeader { my ($refStructInfoEx, $refTypeInfo) = @_; return if not defined $refStructInfoEx->{membersHash}{$refTypeInfo->{name}}->{validonly}; my $condition = GetConditionForSerialize($refStructInfoEx, $refTypeInfo); my $first = $refStructInfoEx->{keys}->[0] eq $refTypeInfo->{name}; my $else = (defined $refStructInfoEx->{union} and not $first) ? "else " : ""; WriteSource "${else}if ($condition)"; WriteSource "{" } sub EmitSerializeValidOnlyFooter { my ($refStructInfoEx, $refTypeInfo) = @_; my $refMembersHash = $refStructInfoEx->{membersHash}; my $footer = ""; $footer = "}" if defined $refMembersHash->{ $refTypeInfo->{name} }->{validonly}; WriteSource $footer; } sub EmitSerializeArray { my ($refStructInfoEx, $refTypeInfo) = @_; my ($countMemberName, $countType, $staticArray) = GetCounterNameAndType($refStructInfoEx, $refTypeInfo); if (not defined $staticArray) { # if pointer is static array, then this check is not needed, since it # always evaluate to false and gcc 12.2 can detect that and issue warning WriteSource "if ($refTypeInfo->{memberName} == NULL || $countMemberName == 0)"; WriteSource "{"; WriteSource "EMIT(\"null\");"; WriteSource "}"; WriteSource "else"; } WriteSource "{"; WriteSource "EMIT(\"[\");\n"; WriteSource "$countType idx;\n"; WriteSource "for (idx = 0; idx < $countMemberName; idx++)"; WriteSource "{"; WriteSource "if (idx != 0)"; WriteSource "{"; WriteSource "EMIT(\",\");"; WriteSource "}\n"; my $passParams = GetPassParamsForSerialize($refStructInfoEx, $refTypeInfo); if ($refTypeInfo->{isattribute}) { WriteSource "const sai_attr_metadata_t *meta ="; WriteSource " sai_metadata_get_attr_metadata($refTypeInfo->{objectType}, $refTypeInfo->{memberName}\[idx\].id);\n"; $passParams = "meta, $passParams"; } my $suffix = $refTypeInfo->{suffix}; my $serializeCall = "sai_serialize_$suffix(buf, $passParams$refTypeInfo->{amp}$refTypeInfo->{memberName}\[idx\])"; my $emitMacro = GetEmitMacroName($refTypeInfo); WriteSource "$emitMacro($serializeCall, $suffix);"; WriteSource "}\n"; WriteSource "EMIT(\"]\");"; WriteSource "}"; } sub IsTypeInfoValid { my ($refStructInfoEx, $refTypeInfo) = @_; my $name = $refTypeInfo->{name}; my $structName = $refStructInfoEx->{name}; my $refMembersHash = $refStructInfoEx->{membersHash}; my $validonly = $refMembersHash->{$name}{validonly}; if ($refStructInfoEx->{keys}[0] eq $name and defined $validonly and not defined $refStructInfoEx->{union}) { # since we don't know if put comma before next key LogError "first key '$name' on '$structName' can't be validonly"; return undef; } if (defined $refStructInfoEx->{union} and not defined $validonly) { LogError "member '$name' in $structName require \@validonly tag"; return undef; } if (defined $refTypeInfo->{union} and not defined $refMembersHash->{$name}{passparam} and not defined $refTypeInfo->{nestedunion}) { LogError "member '$name' in $structName is union, \@passparam tag is required"; return undef; } # TODO if member is struct with extraparam passparam is required if (defined $refTypeInfo->{nestedunion}) { LogError "nested union not supported $structName $name"; return undef; } return 1; } sub ProcessMembersForSerialize { my $refStructInfoEx = shift; my %structInfoEx = %{ $refStructInfoEx }; my $structName = $structInfoEx{name}; my $structBase = $structInfoEx{baseName}; # don't create serialize methods for metadata structs # TODO add tag "noserialize" return if defined $structInfoEx{ismetadatastruct} and $structName ne "sai_object_meta_key_t"; LogDebug "Creating serialize for $structName"; my %membersHash = %{ $structInfoEx{membersHash} }; my @keys = @{ $structInfoEx{keys} }; EmitSerializeFunctionHeader($refStructInfoEx); EmitSerializeHeader; my %processedMembers = (); $refStructInfoEx->{processed} = \%processedMembers; for my $name (@keys) { my $refTypeInfo = GetTypeInfoForSerialize($refStructInfoEx, $name); next if not defined $refTypeInfo; next if not IsTypeInfoValid($refStructInfoEx, $refTypeInfo); EmitSerializeValidOnlyHeader($refStructInfoEx, $refTypeInfo); EmitSerializeMemberKey($refStructInfoEx, $refTypeInfo); if ($refTypeInfo->{ispointer}) { EmitSerializeArray($refStructInfoEx, $refTypeInfo); } else { EmitSerializePrimitive($refStructInfoEx, $refTypeInfo); } EmitSerializeValidOnlyFooter($refStructInfoEx, $refTypeInfo); $refStructInfoEx->{processed}{$name} = 1; } EmitSerializeFooter($refStructInfoEx); } sub CreateSerializeStructs { WriteSectionComment "Serialize structs"; for my $struct (sort keys %main::ALL_STRUCTS) { # user defined serialization next if $struct eq "sai_ip_address_t"; next if $struct eq "sai_ip_prefix_t"; next if $struct eq "sai_attribute_t"; my %structInfoEx = ExtractStructInfoEx($struct, "struct_"); next if defined $structInfoEx{containsfnpointer}; ProcessMembersForSerialize(\%structInfoEx); } } sub CreateSerializeUnions { WriteSectionComment "Serialize unions"; for my $unionTypeName (sort keys %main::SAI_UNIONS) { my %unionInfoEx = ExtractStructInfoEx($unionTypeName, "union_"); ProcessMembersForSerialize(\%unionInfoEx); } } sub CreateSerializeNotifications { WriteSectionComment "Serialize notifications"; for my $ntfName (sort keys %main::NOTIFICATIONS) { ProcessMembersForSerialize($main::NOTIFICATIONS{$ntfName}); } } sub CreateSerializeEmitMacros { WriteSectionComment "Emit macros"; WriteSource "#define EMIT(x) buf += sprintf(buf, x)"; WriteSource "#define EMIT_QUOTE EMIT(\"\\\"\")"; WriteSource "#define EMIT_KEY(k) EMIT(\"\\\"\" k \"\\\":\")"; WriteSource "#define EMIT_NEXT_KEY(k) { EMIT(\",\"); EMIT_KEY(k); }"; WriteSource "#define EMIT_CHECK(expr, suffix) { \\"; WriteSource " ret = (expr); \\"; WriteSource " if (ret < 0) { \\"; WriteSource " SAI_META_LOG_WARN(\"failed to serialize \" #suffix \"\"); \\"; WriteSource " return SAI_SERIALIZE_ERROR; } \\"; WriteSource " buf += ret; }"; WriteSource "#define EMIT_QUOTE_CHECK(expr, suffix) {\\"; WriteSource " EMIT_QUOTE; EMIT_CHECK(expr, suffix); EMIT_QUOTE; }"; } # # DESERIALIZE - Move to separate file, or join with serialize functions # sub CreateDeserializeEmitMacros { WriteSectionComment "Expect macros"; # TODO each expect should free memory current object recursivly on exit # since we know that memory was allocated with zero (except that user one # provided then we need to release memory, actually only in places where we # have count and not const count WriteSource "#define EXPECT(x) { \\"; WriteSource " if (strncmp(buf, x, sizeof(x) - 1) == 0) { buf += sizeof(x) - 1; } \\"; WriteSource " else { \\"; WriteSource " SAI_META_LOG_WARN(\"expected '%s' but got '%.*s...'\", x, (int)sizeof(x), buf); \\"; WriteSource " return SAI_SERIALIZE_ERROR; } }"; WriteSource "#define EXPECT_QUOTE EXPECT(\"\\\"\")"; WriteSource "#define EXPECT_KEY(k) EXPECT(\"\\\"\" k \"\\\":\")"; WriteSource "#define EXPECT_NEXT_KEY(k) { EXPECT(\",\"); EXPECT_KEY(k); }"; WriteSource "#define EXPECT_CHECK(expr, suffix) { \\"; WriteSource " ret = (expr); \\"; WriteSource " if (ret < 0) { \\"; WriteSource " SAI_META_LOG_WARN(\"failed to deserialize \" #suffix \"\"); \\"; WriteSource " return SAI_SERIALIZE_ERROR; } \\"; WriteSource " buf += ret; }"; WriteSource "#define EXPECT_QUOTE_CHECK(expr, suffix) {\\"; WriteSource " EXPECT_QUOTE; EXPECT_CHECK(expr, suffix); EXPECT_QUOTE; }"; } sub EmitDeserializeFunctionHeader { my $refStructInfoEx = shift; my %structInfoEx = %{ $refStructInfoEx }; my $structName = $structInfoEx{name}; my $structBase = $structInfoEx{baseName}; my $membersHash = $structInfoEx{membersHash}; my @keys = @{ $structInfoEx{keys} }; WriteHeader "extern int sai_deserialize_$structBase("; WriteHeader "_In_ const char *buf,"; WriteSource "int sai_deserialize_$structBase("; WriteSource "_In_ const char *buf,"; if (defined $structInfoEx{union} and not defined $structInfoEx{extraparam}) { LogError "union $structName, extraparam required"; return; } # TODO revisit params if (defined $structInfoEx{ismethod}) { # # we create serialize method as this funcion was method instead of # struct, this will be used to create serialize for notifications # for my $name (@keys) { my $type = $membersHash->{$name}{type}; LogDebug "$structName $structBase $name $type"; my $last = 1 if $keys[-1] eq $name; my $end = (defined $last) ? ")" : ","; my $endheader = (defined $last) ? ");" : ","; # TODO pointers WriteSource "_Out_ $type $name$end"; WriteHeader "_Out_ $type $name$endheader"; } } else { if (defined $structInfoEx{extraparam}) { my @params = @{ $structInfoEx{extraparam} }; for my $param (@params) { WriteHeader "_In_ $param,"; WriteSource "_In_ $param,"; } } WriteHeader "_Out_ $structName *$structBase);\n"; WriteSource "_Out_ $structName *$structBase)"; } } sub EmitDeserializeHeader { WriteSource "{"; WriteSource "const char *begin_buf = buf;"; WriteSource "int ret;\n"; WriteSource "EXPECT(\"{\");\n"; } sub EmitDeserializeValidOnlyHeader { my ($refStructInfoEx, $refTypeInfo) = @_; return if not defined $refStructInfoEx->{membersHash}{$refTypeInfo->{name}}->{validonly}; my $condition = GetConditionForSerialize($refStructInfoEx, $refTypeInfo); my $first = $refStructInfoEx->{keys}->[0] eq $refTypeInfo->{name}; my $else = (defined $refStructInfoEx->{union} and not $first) ? "else " : ""; WriteSource "${else}if ($condition)"; WriteSource "{" } sub EmitDeserializeValidOnlyFooter { my ($refStructInfoEx, $refTypeInfo) = @_; my $refMembersHash = $refStructInfoEx->{membersHash}; my $footer = ""; $footer = "}" if defined $refMembersHash->{ $refTypeInfo->{name} }->{validonly}; WriteSource $footer; } sub GetExpectKeyMacroName { my ($refStructInfoEx, $name) = @_; my $firstKey = $refStructInfoEx->{keys}->[0]; return "EXPECT_KEY" if ($firstKey eq $name) or defined $refStructInfoEx->{union}; return "EXPECT_NEXT_KEY"; } sub EmitDeserializeMemberKey { my ($refStructInfoEx, $refTypeInfo) = @_; my $name = $refTypeInfo->{name}; my $expectKeyMacro = GetExpectKeyMacroName($refStructInfoEx, $name); WriteSource "$expectKeyMacro(\"$name\");\n"; } sub GetExpectMacroName { my $refTypeInfo = shift; return "EXPECT_QUOTE_CHECK" if $refTypeInfo->{needQuote}; return "EXPECT_CHECK"; } sub GetPassParamsForDeserialize { my ($refStructInfoEx, $refTypeInfo) = @_; my $name = $refTypeInfo->{name}; my $refMembersHash = $refStructInfoEx->{membersHash}; return "" if not defined $refMembersHash->{$name}{passparam}; my $structBase = $refStructInfoEx->{baseName}; my @params = @{ $refMembersHash->{$name}{passparam} }; my $passParams = ""; for my $param (@params) { if (not $param =~ /->/ and defined $refMembersHash->{$param}) { $passParams .= "$structBase\->$param, "; } else { $passParams .= "$param, "; } } return $passParams; } sub EmitDeserializePrimitive { my ($refStructInfoEx, $refTypeInfo) = @_; my $suffix = $refTypeInfo->{suffix}; my $emitMacro = GetExpectMacroName($refTypeInfo); my $passParams = GetPassParamsForDeserialize($refStructInfoEx, $refTypeInfo); my $amp = $refTypeInfo->{deamp}; my $serializeCall = "sai_deserialize_$suffix(buf, $passParams$amp$refTypeInfo->{memberName})"; WriteSource "$emitMacro($serializeCall, $suffix);"; } sub EmitDeserializeFooter { my $refStructInfoEx = shift; if (defined $refStructInfoEx->{union}) { my $name = $refStructInfoEx->{name}; WriteSkipForMask() if $name eq "sai_acl_field_data_mask_t"; # if it's union, we must check if we serialized something # (not always true for acl mask) WriteSource "else"; WriteSource "{"; WriteSource "SAI_META_LOG_WARN(\"nothing was deserialized for '$name', bad condition?\");"; # WriteSource "return SAI_SERIALIZE_ERROR;"; WriteSource "return SAI_SERIALIZE_ERROR;" if $name eq "sai_attribute_value_t"; WriteSource "}\n"; } WriteSource "EXPECT(\"}\");\n"; WriteSource "return (int)(buf - begin_buf);"; WriteSource "}"; } sub EmitDeserializeArray { my ($refStructInfoEx, $refTypeInfo) = @_; my ($countMemberName, $countType) = GetCounterNameAndType($refStructInfoEx, $refTypeInfo); if (not $countMemberName =~ /^$NUMBER_REGEX$/) { WriteSource "if (strncmp(buf, \"null\", 4) == 0)"; WriteSource "{"; WriteSource "$refTypeInfo->{memberName} = NULL;\n"; WriteSource "buf += 4;"; WriteSource "}"; WriteSource "else"; } WriteSource "{"; # # if count is const, we dont need to allocate memory since is a buffer like # char[32] or something similar # if (not $countMemberName =~ /^$NUMBER_REGEX$/) { WriteSource "$refTypeInfo->{memberName} = calloc(($countMemberName), sizeof($refTypeInfo->{noptrtype}));\n"; } WriteSource "EXPECT(\"[\");\n"; WriteSource "$countType idx;\n"; WriteSource "for (idx = 0; idx < $countMemberName; idx++)"; WriteSource "{"; WriteSource "if (idx != 0)"; WriteSource "{"; WriteSource "EXPECT(\",\");"; WriteSource "}\n"; my $passParams = GetPassParamsForSerialize($refStructInfoEx, $refTypeInfo); if ($refTypeInfo->{isattribute}) { # # NOTE: deserialize attribute not require metadata since we have user # provided deserialize, and serialized attr id points to exact metadata # # WriteSource "const sai_attr_metadata_t *meta ="; # WriteSource " sai_metadata_get_attr_metadata($refTypeInfo->{objectType}, $refTypeInfo->{memberName}\[idx\].id);\n"; # $passParams = "meta, $passParams"; } my $amp = $refTypeInfo->{deamp}; my $suffix = $refTypeInfo->{suffix}; my $serializeCall = "sai_deserialize_$suffix(buf, $passParams$amp$refTypeInfo->{memberName}\[idx\])"; my $emitMacro = GetExpectMacroName($refTypeInfo); WriteSource "$emitMacro($serializeCall, $suffix);"; WriteSource "}\n"; WriteSource "EXPECT(\"]\");"; WriteSource "}"; } # TODO in case of failure we need to recursivly free memory that we allocated # to prevent memory leak sub ProcessMembersForDeserialize { my $refStructInfoEx = shift; my %structInfoEx = %{ $refStructInfoEx }; my $structName = $structInfoEx{name}; my $structBase = $structInfoEx{baseName}; # don't create serialize methods for metadata structs # TODO add tag "noserialize" return if defined $structInfoEx{ismetadatastruct} and $structName ne "sai_object_meta_key_t"; LogDebug "Creating deserialize for $structName"; my %membersHash = %{ $structInfoEx{membersHash} }; my @keys = @{ $structInfoEx{keys} }; EmitDeserializeFunctionHeader($refStructInfoEx); EmitDeserializeHeader(); my %processedMembers = (); $refStructInfoEx->{processed} = \%processedMembers; for my $name (@keys) { my $refTypeInfo = GetTypeInfoForSerialize($refStructInfoEx, $name); next if not defined $refTypeInfo; next if not IsTypeInfoValid($refStructInfoEx, $refTypeInfo); EmitDeserializeValidOnlyHeader($refStructInfoEx, $refTypeInfo); EmitDeserializeMemberKey($refStructInfoEx, $refTypeInfo); if ($refTypeInfo->{ispointer}) { EmitDeserializeArray($refStructInfoEx, $refTypeInfo); } else { EmitDeserializePrimitive($refStructInfoEx, $refTypeInfo); } EmitDeserializeValidOnlyFooter($refStructInfoEx, $refTypeInfo); $refStructInfoEx->{processed}{$name} = 1; } EmitDeserializeFooter($refStructInfoEx); } sub CreateDeserializeStructs { WriteSectionComment "Deserialize structs"; for my $struct (sort keys %main::ALL_STRUCTS) { # user defined deserialization next if $struct eq "sai_ip_address_t"; next if $struct eq "sai_ip_prefix_t"; next if $struct eq "sai_attribute_t"; my %structInfoEx = ExtractStructInfoEx($struct, "struct_"); next if defined $structInfoEx{containsfnpointer}; ProcessMembersForDeserialize(\%structInfoEx); } } sub CreateDeserializeForEnums { WriteSectionComment "Enum deserialize methods"; for my $key (sort keys %main::SAI_ENUMS) { next if $key =~ /_attr_t$/; if (not $key =~ /^sai_(\w+)_t$/) { LogWarning "wrong enum name '$key'"; next; } my $suffix = $1; WriteHeader "extern int sai_deserialize_$suffix("; WriteHeader "_In_ const char *buffer,"; WriteHeader "_Out_ $key *$suffix);\n"; WriteSource "int sai_deserialize_$suffix("; WriteSource "_In_ const char *buffer,"; WriteSource "_Out_ $key *$suffix)"; WriteSource "{"; WriteSource "return sai_deserialize_enum(buffer, &sai_metadata_enum_$key, (int*)$suffix);"; WriteSource "}"; } } sub CreateDeserializeUnions { WriteSectionComment "Deserialize unions"; for my $unionTypeName (sort keys %main::SAI_UNIONS) { my %unionInfoEx = ExtractStructInfoEx($unionTypeName, "union_"); ProcessMembersForDeserialize(\%unionInfoEx); } } sub CreateSerializeMethods { CreateSerializeForEnums(); CreateSerializeEmitMacros(); CreateSerializeStructs(); CreateSerializeNotifications(); CreateSerializeUnions(); CreateDeserializeForEnums(); CreateDeserializeEmitMacros(); CreateDeserializeStructs(); CreateDeserializeUnions(); # TODO deserialize notifications } BEGIN { our @ISA = qw(Exporter); our @EXPORT = qw/ CreateSerializeMethods /; } 1; # TODO we could also generate deserialize and call notifation where # notification struct would be passed and notification would be called and then # free itself (but on exception there will be memory leak) # # TODO generate notifications metadata, if param is object id then objects must # be specified for validation wheter notification returned valid object, and # each struct that is using object id should have @objects on object id # members, then we should generate all struct infos to get all functions for # oid extraction etc # # TODO validate if "validonly" param is not validonly struct/union and in union # validonly must be param not any union member # # TODO support enum_list 32 - on unions and acl capability list force to add # @type wheter enum or int32 / int32_list for those types # # TODO generate deserialize # # TODO generate validate - object types and enums, also objecttype in union # passed from params must be forced to add, in sai_list32_oid - any add special # case # # TODO generate transfer methods # # TODO since we need object type for validation on notification params, then we # need notifications metadata, is object_id, allowed object types, is # attribute, is pointer, etc # # TODO generate serialize and deserialize versions with only count # # TODO force unions to serialize something + add exception on serialize for # mask @flags serialize:allowempty # # TODO validate if count is not pointer when serializing counts # # TODO we could generate serialize methods for all api functions since we have # metadata now and final step would be to generate RPC client/server for SAI