Subject: [rohc] FN let statements and TCP packet formats (was: Updated FN Draft - Appendix A)

From: "Finking, Robert" <[email-masked]>
Date: Wed, 6 Apr 2005 16:19:48 +0100

> You say that sometimes you do not know whether it will be only an
> "if", but I have not seen an example of when this is really needed.
> I'm still waiting for a realistic example of when one would have
> to have that. Just because the current definition of let allows that
> does not mean it is really needed.

OK, so let's go for proof by contradiction. I'll go through the current
tcp-profile and replace each occurrence of "let" with either an assignment,
or an if statement. Then if the meaning is changed from that in the current
profile, somebody needs to shout up and say why it's wrong. Otherwise I
agree with you that "if" and assignment are clearer: whilst it goes against
the grain for me personally, is probably the better solution.

To be as clear as possible, I've used "when" for the non-binding conditional
case, and ordinary assignment syntax (i.e. single equals sign) for
assignment. Here goes:

static_or_irreg32(flag) ===
{
        uc_format = field; %[ 32 ]
   
        co_format_irreg_enc = field, %[ 32 ]
        {
           when (flag == 1);
           field ::= irregular(32);
        };
        
        co_format_static_enc = field, %[ 0 ]
        {
           when (flag == 0);
           field ::= static;
        };
};

Similarly for static_or_irreg16(flag), static_or_irreg8(flag).

variable_length_32_enc(flag) ===
{
        uc_format = field; %[ 32 ]

        co_format_not_present = field, %[ 0 ]
        {
           when(flag == 0);
           field ::= static;
        };
        co_format_8_bit = field, %[ 8 ]
        {
           when(flag == 1);
           field ::= lsb(8, 63);
        };
        co_format_16_bit = field, %[ 16 ]
        {
           when(flag == 2);
           field ::= lsb(16, 16383);
        };
        co_format_32_bit = field, %[ 32 ]
        {
           when(flag == 3);
           field ::= irregular(32);
        };
};

Similarly for variable_length_16_enc(flag).

optional32(flag) ===
{
        uc_format = item; % 0 or 32 bits

        co_format_present = item, %[ 32 ]
        {
           when(flag == 1);
           item ::= irregular (32);
        };

        co_format_not_present = item, %[ 0 ]
        {
           when(flag == 0);
           item ::= compressed_value (0, 0);
        };
};

opt_lsb_7_or_31 (flag) ===
{
        uc_format = item; % 32 bits

        co_format_present = item, % 8 or 32 bits
        {
           when (flag == 1);
           item ::= lsb_7_or_31;
        };

        co_format_not_present = item, %[ 0 ]
        {
           when (flag == 0);
           item ::= compressed_value (0, 0);
        };
};

optional_checksum (flag_value) ===
{
        uc_format = value,     % 0 or 16 bits
                    reserved1; % 0 or 16 bits

        co_format_cs_present = value,     %[ 16 ]
                               reserved1, %[ 0 ]
        {
           when(flag_value == 1);
           value     ::= irregular (16);
           reserved1 ::= uncompressed_value (16, 0);
        };

        co_format_not_present = value,     %[ 0 ]
                                reserved1, %[ 0 ]
        {
           when(flag_value == 0);
           value     ::= compressed_value (0, 0);
           reserved1 ::= compressed_value (0, 0);
        };
};

irreg_tos_tc ===
{
        uc_format = tos_tc; %[ 6 ]

        co_format_tos_tc_present = tos_tc, %[ 6 ]
        {
           when(ecn_used:uncomp_value == 1);
           tos_tc  ::= irregular (6);
        };

        co_format_tos_tc_not_present = tos_tc, %[ 0 ]
        {
           when(ecn_used:uncomp_value == 0);
           tos_tc  ::= static;
        };
};

Similarly for ip_irreg_ecn.

        co_format_ipv6_outer_irregular_without_ttl
                                    = tos_tc,       % 0 or 6 bits
                                      ip_ecn_flags, % 0 or 2 bits
        {
                % for 'outer' headers only, irregular chain is required
                tos_tc        ::= irreg_tos_tc;
                ip_ecn_flags  ::= ip_irreg_ecn;
                when(ttl_irregular_chain_flag == 0);
        };

        co_format_ipv6_outer_irregular_with_ttl
                                    = tos_tc,       % 0 or 6 bits
                                      ip_ecn_flags, % 0 or 2 bits
                                      ttl_hopl,     %[ 8 ]
        {
                % for 'outer' headers only, irregular chain is required
                tos_tc        ::= irreg_tos_tc;
                ip_ecn_flags  ::= ip_irreg_ecn;
                when(ttl_irregular_chain_flag == 1);
                ttl_hopl       ::= irregular(8);
        };

        % Note that the ECN bits are stored in the global control field
        % so that they can be output in TCP irregular chain.
        co_format_ipv6_innermost_irregular = 
        {
          ip_inner_ecn:uncomp_value = ip_ecn_flags:uncomp_value;
        };

ip_id_enc_dyn (behavior) ===
{
        uc_format = ip_id; %[ 16 ]

        co_format_ip_id_seq = ip_id, %[ 16 ]
        {
              when((behavior == 0) || (behavior == 1) || (behavior == 2));
              % In dynamic chain, but random, seq, and seq-swapped are 16
bits
              ip_id ::= irregular(16);
        };

        co_format_ip_id_zero = ip_id, %[ 0 ]
        {
              when(behavior == 3);
              % Zero IPID
              ip_id ::= uncompressed_value (16, 0);
        };
};

ip_id_enc_irreg (behavior) ===
{
        uc_format = ip_id; %[ 16 ]

        co_format_ip_id_seq = ip_id, %[ 0 ]
        {
           when(behavior == 0); % sequential
           ip_id ::= static;    % Nothing to send in irregular chain
        };

        co_format_ip_id_seq_swapped = ip_id, %[ 0 ]
        {
           when(behavior == 1); % sequential-swapped
           ip_id ::= static;    % Nothing to send in irregular chain
        };
        
        co_format_ip_id_rand = ip_id, %[ 16 ]
        {
           when(behavior == 2); % random
           ip_id ::= irregular (16);
        };

        co_format_ip_id_zero = ip_id, %[ 0 ]
        {
           when(behavior == 3); % zero
           ip_id ::= uncompressed_value (16, 0);
        };
};

ip_id_behavior_enc ===
{
      uc_format = ip_id_behavior; %[ 2 ]

	default_methods =
	{
	   ip_id_behavior ::= irregular(2);
	};

      co_format_sequential = ip_id_behavior, %[ 2 ]
      {
	  ip_id_behavior:uncomp_value = 0b00;
      };

      co_format_sequential_swapped = ip_id_behavior, %[ 2 ]
      {
	  ip_id_behavior:uncomp_value = 0b01;
      };

      co_format_random = ip_id_behavior, %[ 2 ]
      {
	  ip_id_behavior:uncomp_value = 0b10;
      };

      co_format_zero = ip_id_behavior, %[ 2 ]
      {
	  ip_id_behavior:uncomp_value = 0b11;
      };
};

As an aside, I think this format has a bug in it (see separate post).

        co_format_ipv4_outer_irregular_without_ttl =
                                      ip_id,        % 0 or 16 bits
                                      tos_tc,       % 0 or 6 bits
                                      ip_ecn_flags, % 0 or 2 bits
        {
                ip_id_behavior ::= static;
                ip_id          ::=
ip_id_enc_irreg(ip_id_behavior:uncomp_value);
                tos_tc         ::= irreg_tos_tc;
                ip_ecn_flags   ::= ip_irreg_ecn;
                when(ttl_irregular_chain_flag == 0);
        };

        co_format_ipv4_outer_irregular_with_ttl =
                                      ip_id,        % 0 or 16 bits
                                      tos_tc,       % 0 or 6 bits
                                      ip_ecn_flags, % 0 or 2 bits
                                      ttl_hopl,     %[ 8 ]
        {
                ip_id_behavior ::= static;
                ip_id          ::=
ip_id_enc_irreg(ip_id_behavior:uncomp_value);
                tos_tc         ::= irreg_tos_tc;
                ip_ecn_flags   ::= ip_irreg_ecn;
                when(ttl_irregular_chain_flag == 1);
                ttl_hopl       ::= irregular(8);
        };

        % Note that the ECN bits are stored in the global control field
        % so that they can be output in TCP irregular chain.
        co_format_ipv4_innermost_irregular = ip_id, % 0 or 16 bits
        {
                ip_id_behavior ::= static;
                ip_id          ::= ip_id_enc_irreg
(ip_id_behavior:uncomp_value);
                ip_inner_ecn:uncomp_value = ip_ecn_flags:uncomp_value;
        };

tcp_opt_eol(nbits) ===
{
        uc_format = type,    %[ 8 ]
                    padding; % (nbits - 8) bits

        default_methods =
        {
                type    ::= uncompressed_value(8, 0);
                pad_len ::= static;
                padding ::= uncompressed_value (nbits - 8, 0);
        };

        co_format_eol_list_item = pad_len, %  8 bits
                                  padding, %[ 0 ]
        {
                pad_len ::= compressed_value(8, nbits - 8);
        };

        co_format_eol_irregular =
        {
                pad_len:uncomp_value = nbits - 8;
        };
};

Again I think this has a bug in it in that the default static encoding will
never be used.

sack_var_length_enc (base) ===
{
        uc_format = sack_field; %[ 32 ]

        default_methods =
        {
                sack_offset:uncomp_value  = sack_field:uncomp_value - base;
                sack_offset:uncomp_length = 32;
                sack_field:uncomp_length  = 32;
        };

        co_format_lsb_15 = discriminator, %[ 1 ]
                           sack_offset,   %[ 15 ]
        {
                discriminator    ::= '0';
                sack_offset      ::= lsb (15, -1);
        };

        co_format_lsb_22 = discriminator, %[ 2 ]
                           sack_offset,   %[ 22 ]
        {
                discriminator    ::= '10';
                sack_offset      ::= lsb (22, -1);
        };

        co_format_lsb_30 = discriminator, %[ 2 ]
                           sack_offset,   %[ 30 ]
        {
                discriminator    ::= '11';
                sack_offset      ::= lsb (30, -1);
        };
};

        co_format_sack1_list_item = discriminator,
                                    block_1,
        {
                when(length:uncomp_value == 10);
                discriminator ::= '00000001';
                block_1 ::= tcp_opt_sack_block (ack_value);
        };

Similarly for other sack list items

tcp_opt_generic ===
{
        uc_format = type,       %[ 8 ]
                    length_msb, %[ 1 ]
                    length_lsb, %[ 7 ]
                    contents;   % n bits

        control_fields = option_static; %[ 1 ]                        

        default_methods =
        {
                type      ::= static;
                %
                % lengths are always < 128
                % (i.e. the msb is always 0)
                %
                length_msb    ::= uncompressed_value (1, 0);
                length_lsb    ::= static;
                contents      ::= static;
		
		    option_static:uncomp_length = 1;
        };

        co_format_generic_list_item = type,          %[ 8 ]
                                      option_static, %[ 1 ]
                                      length_lsb,    %[ 7 ]
                                      contents,      % n bits
        {
                type          ::= irregular (8);
                option_static ::= irregular (1);
                length_lsb    ::= irregular (7);
                contents  ::= irregular (length_len:uncomp_value * 8 - 16);
        };

        % Used when context of option has option_static set to one
        co_format_generic_irregular_static =
        {
                option_static:uncomp_value = 1;
        };

        % An item that can change, but currently is unchanged
        co_format_generic_irregular_stable = discriminator, %[ 8 ]
        {
                option_static:uncomp_value = 0;
                discriminator ::= '11111111';
        };
        
        % An item that can change, and has changed compared to context.
        % Length is not allowed to change here, since a length change is
        % most likely to cause new NOPs or an EOL length change.
        co_format_generic_irregular_full = discriminator, %[ 8 ]
                                           contents,      % n bits
        {
                option_static:uncomp_value = 0;
                discriminator ::= '00000000';
                contents      ::= irregular (length_lsb:uncomp_value * 8 -
16);
        };

};

list_tcp_options(nbits, ack_value) ===     {
        uc_format = item, tail;

        default_methods = {
           when(nbits >= item:uncomp_length);
           tail ::= list_tcp_options(nbits - item:uncomp_length, ack_value);
        };

        co_format_list_end =       {
           when(nbits == 0);
           item ::= irregular(0);
           tail ::= irregular(0);
        };

        co_format_eol = item, tail       {
           when(nbits == item:uncomp_length); % redundant
           item ::= tcp_opt_eol(nbits);
           tail ::= irregular(0);
        };
          :
          :

Again, I think the "when" in default_methods here should be moved to the
uc_format encodings list.

tcp_list_presence_enc(list_length, presence, ack_value) ===
{
        uc_format = tcp_options;

        co_format_list_not_present = tcp_options, %[ 0 ] 
        {
           when(presence == 0);
           tcp_options ::= static;
        };

        co_format_list_present = tcp_options, % 8 + n*8 bits
        {
           when(presence == 1);
           tcp_options ::= list_tcp_options(list_length, ack_value);
        };
};

port_replicate(flags) ===
{
        uc_format  =   port;        %[ 16 ]

        co_format_port_static_enc   = port,      %[ 0 ]
        {
           when(flags == 0b00);
           port          ::= static;
        };

        co_format_port_lsb8         = port,      %[ 8 ]
        {
           when(flags == 0b01);
           port          ::= lsb (8, 64);
        };

        co_format_port_irr_enc      = port,      %[ 16 ]
        {
           when(flags == 0b10);
           port          ::= irregular (16);
        };
};

zero_or_irr16_enc(flag) ===
{
        uc_format = field; %[ 16 ]

        co_format_non_zero = field, %[ 16 ]
        {
           when(flag == 0);
           field ::= irregular (16);
        };

        co_format_zero = field, %[ 0 ]
        {
           when(flag == 1);
           field ::= uncompressed_value (16, 0);
        };
};

ack_enc_dyn(flag) ===
{
        uc_format = ack_number; %[ 32 ]

        co_format_ack_non_zero = ack_number, %[ 32 ]
        {
           when(flag == 0);
           ack_number ::= irregular (32);
        };

        co_format_ack_zero = ack_number, %[ 0 ]
        {
           when(flag == 1);
           ack_number ::= uncompressed_value (32, 0);
        };
};

tcp_ecn_flags_enc ===
{
        uc_format = tcp_ecn_flags; %[ 2 ]

        co_format_irreg = tcp_ecn_flags, %[ 2 ]
        {
           when(ecn_used:uncomp_value == 1);
           tcp_ecn_flags ::= irregular(2);
        };

        co_format_unused =
        {
           when(ecn_used:uncomp_value == 0);
           tcp_ecn_flags ::= static;
        };
};

tcp_res_flags_enc ===
{
        uc_format = tcp_res_flags; %[ 4 ]

        co_format_irreg = tcp_res_flags, %[ 4 ]
        {
           when(ecn_used:uncomp_value == 1);
           tcp_res_flags     ::= irregular(4);
        };

        co_format_unused =
        {
           when(ecn_used:uncomp_value == 0);
           tcp_res_flags     ::= uncompressed_value(4, 0);
        };
};

tcp_irreg_ip_ecn ===
{
        uc_format = ip_ecn_flags;     %[ 2 ]

        co_format_tc_present = ip_ecn_flags,       %[ 2 ]
        {
           when(ecn_used:uncomp_value == 1);
           ip_ecn_flags ::= compressed_value(2, ip_inner_ecn:uncomp_value);
        };

        co_format_tc_not_present = ip_ecn_flags, %[ 0 ]
        {
           when(ecn_used:uncomp_value == 0);
           ip_inner_ecn ::= static; % Global control field
           ip_ecn_flags ::= compressed_value(0,0); % Nothing transmit
        };
};

optional_2bit_padding(used_flag) ===
{
        uc_format = ;

        co_format_used = padding, %[ 2 ]
        {
                when(used_flag == 1);
                padding ::= compressed_value (2, 0x0);
        };

        co_format_unused = padding,
        {
                when(used_flag == 0);
                padding ::= compressed_value (0, 0x0);
        };

};

tos_tc_enc(flag) ===
{
        uc_format = tos_tc; %[ 6 ]
    
        co_format_static = tos_tc, %[ 0 ]
        {
           when(flag == 0);
           tos_tc             ::= static;
        };
    
        co_format_irreg = tos_tc,  %[ 6 ]
                          padding, %[ 2 ]
        {
           when(flag == 1);
           tos_tc             ::= irregular(6);
           padding            ::= compressed_value (2, 0);
        };
};

ip_id_lsb (behavior, k, p) ===
{
        uc_format = ip_id; %[ 16 ]

        default_methods =
        {
          ip_id:uncomp_length == 16;
        };

        co_format_nbo = ip_id_offset, % k bits
        {
           when(behavior == 0);
           ip_id_offset:uncomp_value = ip_id:uncomp_value 
                                       - msn:uncomp_value;
           ip_id_offset:uncomp_length = 16;

           ip_id_offset ::= lsb (k, p);
        };

        co_format_non_nbo = ip_id_offset, % k bits
        {
           when(behavior == 1);

           ip_id_nbo:uncomp_value = (ip_id:uncomp_value / 256)
                                    + (ip_id:uncomp_value & 255) * 256;
           ip_id_nbo:uncomp_length = 16;
           ip_id_offset:uncomp_value = ip_id_nbo:uncomp_value
                                       - msn:uncomp_value;
           ip_id_offset:uncomp_length = 16;

           ip_id_offset ::= lsb (k, p);
        };
};

Note there is an error here - the ip_id_offset is not specified as a control
field and yet it has an uncompressed side, which needs declaring. The above
example however is a great illustration of the benefit of L-E's idea - it
makes the profile considerably easier to read (IMHO).

dont_fragment(version) ===
{
        uc_format = df; %[ 1 ]
   
        co_format_v4 = df, %[ 1 ]
        {
           when(version == 4);
           df ::= irregular(1);
        };
   
        co_format_v6 = df,
        {
           when(version == 6);
           df ::= compressed_value(1,0);
        };
};

co_baseheader(payload_size, ttl_irregular_chain_flag) ===
{
   uc_format_v4 = version,        %[  4 ]
                  header_length,  %[  4 ]
                       :
                  options_list,   % n bits
   {
      when(version:uncomp_value == 4);
   };

   uc_format_v6 = version,        %[   4 ]
                  tos_tc,         %[   6 ]
                     :
                  options_list,   % n bits
   {
      when(version:uncomp_value == 6);
   };

   control_fields  = ip_id_behavior;   % 2 bits
                              
   default_methods = 
   {
      version                      ::= static;
      tos_tc                       ::= static;
         :
         :
      version:uncomp_length = 4;

      seq_number_scaled:uncomp_value =
               seq_number:uncomp_value / payload_size;
      seq_number_residue:uncomp_value =
               mod(seq_number:uncomp_value, payload_size);
      ack_number:uncomp_value =
               (ack_stride:uncomp_value * ack_number_scaled:uncomp_value) +
               ack_number_residue:uncomp_value;
      ack_number_residue:uncomp_value =
               mod(ack_number:uncomp_value, ack_stride:uncomp_value);
   };

   co_format_co_common = discriminator,        %[ 7 ]
                         ttl_hopl_outer_flag,  %[ 1 ]
                               :
                               :
   {
      discriminator      ::= '1111101';
      
      ttl_hopl_outer_flag::= irregular(1);
      % Need to bind argument so that user can pass it on to the
      % structure for IPv4/IPv6 irregular chain.
      ttl_irregular_chain_flag = ttl_hopl_outer_flag:uncomp_value;
       :
       :
   };

   % Send LSBs of sequence number
   co_format_rnd_1 = discriminator, %[ 8 ]
                     seq_number,    %[ 16 ]
                     msn,           %[ 4 ]
                     psh_flag,      %[ 1 ]
                     header_crc,    %[ 3 ]
   {
      when((ip_id_behavior:uncomp_value == 2) ||
           (ip_id_behavior:uncomp_value == 3));
      discriminator       ::= '10111110';
      msn                 ::= lsb(4, 4);
      header_crc          ::= crc3 (this:uncomp_value,
                                    this:uncomp_length);
      psh_flag            ::= irregular (1);
      seq_number          ::= lsb(16, 32767);
   };

similarly for all other co_formats

_______________________________________________
Rohc mailing list
[email-masked]
https://www1.ietf.org/mailman/listinfo/rohc

---

From: Carsten Bormann <[email-masked]>
Date: Wed, 6 Apr 2005 20:03:12 +0200

>           ip_inner_ecn:uncomp_value = ip_ecn_flags:uncomp_value;

Why not

ip_ecn_flags:uncomp_value = ip_inner_ecn:uncomp_value;

>                 pad_len:uncomp_value = nbits - 8;

Why not
                 nbits = pad_len:uncomp_value + 8;

I still don't understand what we are trying to achieve here.

I have no problem leaving out the "when" (the parentheses did help 
syntax analysis; I don't think really bad things happen if you leave 
them out).
If people think it includes readability, we could allow "=" as a 
synonym of "==" (although then I'd like to change the separator we use 
between format names and field lists).

Gruesse, Carsten