示例#1
0
文件: row.cpp 项目: iMax3060/zero
void table_row_t::load_value(char* data, index_desc_t* pindex)
{
    // Read the data field by field
    assert (data);

    // 1. Get the pre-calculated offsets

    // current offset for fixed length field values
    offset_t fixed_offset = get_fixed_offset();

    // current offset for variable length field slots
    offset_t var_slot_offset = get_var_slot_offset();

    // current offset for variable length field values
    offset_t var_offset = get_var_offset();


    // 2. Read the data field by field
    int null_index = -1;
    for (unsigned i=0; i<_ptable->field_count(); i++) {

        if (pindex) {
            bool skip = false;
            // skip key fields
            for (unsigned j = 0; j < pindex->field_count(); j++) {
                if ((int) i == pindex->key_index(j)) {
                    skip = true;
                    break;
                }
            }
            if (skip) continue;
        }

        // Check if the field can be NULL.
        // If it can be NULL, increase the null_index,
        // and check if the bit in the null_flags bitmap is set.
        // If it is set, set the corresponding value in the tuple
        // as null, and go to the next field, ignoring the rest
	if (_pvalues[i].field_desc()->allow_null()) {
	    null_index++;
	    if (IS_NULL_FLAG(data, null_index)) {
		_pvalues[i].set_null();
		continue;
	    }
	}

        // Check if the field is of VARIABLE length.
        // If it is, copy the offset of the value from the offset part of the
        // buffer (pointed by var_slot_offset). Then, copy that many chars from
        // the variable length part of the buffer (pointed by var_offset).
        // Then increase by one offset index, and offset of the pointer of the
        // next variable value
	if (_pvalues[i].is_variable_length()) {
	    offset_t var_len;
	    memcpy(&var_len,  VAR_SLOT(data, var_slot_offset), sizeof(offset_t));
            _pvalues[i].set_value(data+var_offset, var_len);
	    var_offset += var_len;
	    var_slot_offset += sizeof(offset_t);
	}
	else {
            // If it is of FIXED length, copy the data from the fixed length
            // part of the buffer (pointed by fixed_offset), and the increase
            // the fixed offset by the (fixed) size of the field
            _pvalues[i].set_value(data+fixed_offset,
                    _pvalues[i].maxsize());
	    fixed_offset += _pvalues[i].maxsize();
	}
    }
}
示例#2
0
文件: row.cpp 项目: iMax3060/zero
void table_row_t::store_value(char* data, size_t& length, index_desc_t* pindex)
{
    // 1. Get the pre-calculated offsets

    // current offset for fixed length field values
    offset_t fixed_offset = get_fixed_offset();

    // current offset for variable length field slots
    offset_t var_slot_offset = get_var_slot_offset();

    // current offset for variable length field values
    offset_t var_offset = get_var_offset();



    // 2. calculate the total space of the tuple
    //   (tupsize)    : total space of the tuple

    int tupsize    = 0;

    int null_count = get_null_count();
    int fixed_size = get_var_slot_offset() - get_fixed_offset();

    // loop over all the variable-sized fields and add their real size (set at ::set())
    for (unsigned i=0; i<_ptable->field_count(); i++) {
	if (_pvalues[i].is_variable_length()) {
            // If it is of VARIABLE length, then if the value is null
            // do nothing, else add to the total tuple length the (real)
            // size of the value plus the size of an offset.

            if (_pvalues[i].is_null()) continue;
            tupsize += _pvalues[i].realsize();
            tupsize += sizeof(offset_t);
	}

        // If it is of FIXED length, then increase the total tuple
        // length, as well as, the size of the fixed length part of
        // the tuple by the fixed size of this type of field.

        // IP: The length of the fixed-sized fields is added after the loop
    }

    // Add up the length of the fixed-sized fields
    tupsize += fixed_size;

    // In the total tuple length add the size of the bitmap that
    // shows which fields can be NULL
    if (null_count) tupsize += (null_count >> 3) + 1;
    assert (tupsize);

    if ((long) length < tupsize) {
        throw runtime_error("Tuple does not fit on allocated buffer");
    }
    length = tupsize;

    // 4. Copy the fields to the array, field by field

    int null_index = -1;
    // iterate over all fields
    for (unsigned i=0; i<_ptable->field_count(); i++) {

        // skip fields which are part of the given index
        if (pindex) {
            bool skip = false;
            for (unsigned j=0; j<pindex->field_count(); j++) {
                if ((int) i == pindex->key_index(j)) {
                    skip = true;
                    break;
                }
            }
            if (skip) continue;
        }

        // Check if the field can be NULL.
        // If it can be NULL, increase the null_index, and
        // if it is indeed NULL set the corresponding bit
	if (_pvalues[i].field_desc()->allow_null()) {
	    null_index++;
	    if (_pvalues[i].is_null()) {
		SET_NULL_FLAG(data, null_index);
	    }
	}

        // Check if the field is of VARIABLE length.
        // If it is, copy the field value to the variable length part of the
        // buffer, to position  (buffer + var_offset)
        // and increase the var_offset.
	if (_pvalues[i].is_variable_length()) {
            _pvalues[i].copy_value(data + var_offset);
            int offset = _pvalues[i].realsize();
	    var_offset += offset;

            // set the offset
            offset_t len = offset;
            memcpy(VAR_SLOT(data, var_slot_offset), &len,
                    sizeof(offset_t));
	    var_slot_offset += sizeof(offset_t);
	}
	else {
            // If it is of FIXED length, then copy the field value to the
            // fixed length part of the buffer, to position
            // (buffer + fixed_offset)
            // and increase the fixed_offset
            _pvalues[i].copy_value(data + fixed_offset);
	    fixed_offset += _pvalues[i].maxsize();
	}
    }
}
示例#3
0
void StreamVm::build_program(){

    /* build the commands into a buffer */
    m_instructions.clear();
    int var_cnt=0;
    clean_max_field_cnt();
    uint32_t ins_id=0;

    for (auto inst : m_inst_list) {
        StreamVmInstruction::instruction_type_t ins_type=inst->get_instruction_type();

        /* itFIX_IPV4_CS */
        if (ins_type == StreamVmInstruction::itFIX_IPV4_CS) {
            StreamVmInstructionFixChecksumIpv4 *lpFix =(StreamVmInstructionFixChecksumIpv4 *)inst;

            if ( (lpFix->m_pkt_offset + IPV4_HDR_LEN) > m_pkt_size  ) {

                std::stringstream ss;
                ss << "instruction id '" << ins_id << "' fix ipv4 command offset  " << lpFix->m_pkt_offset << "  is too high relative to packet size  "<< m_pkt_size;
                err(ss.str());
            }

            uint16_t offset_next_layer = IPV4_HDR_LEN;

            if ( m_pkt ){
                IPHeader * ipv4= (IPHeader *)(m_pkt+lpFix->m_pkt_offset);
                offset_next_layer = ipv4->getSize();
            }

            if (offset_next_layer<IPV4_HDR_LEN) {
                offset_next_layer=IPV4_HDR_LEN;
            }

            if ( (lpFix->m_pkt_offset + offset_next_layer) > m_pkt_size  ) {

                std::stringstream ss;
                ss << "instruction id '" << ins_id << "' fix ipv4 command offset  " << lpFix->m_pkt_offset << "plus "<<offset_next_layer<< " is too high relative to packet size  "<< m_pkt_size;
                err(ss.str());
            }
            /* calculate this offset from the packet */
            add_field_cnt(lpFix->m_pkt_offset + offset_next_layer);

            StreamDPOpIpv4Fix ipv_fix;
            ipv_fix.m_offset = lpFix->m_pkt_offset;
            ipv_fix.m_op = StreamDPVmInstructions::ditFIX_IPV4_CS;
            m_instructions.add_command(&ipv_fix,sizeof(ipv_fix));
        }


        /* flow man */
        if (ins_type == StreamVmInstruction::itFLOW_MAN) {
            StreamVmInstructionFlowMan *lpMan =(StreamVmInstructionFlowMan *)inst;

            var_cnt++;

            if (lpMan->m_size_bytes == 1 ){
                if ( (lpMan->m_step == 1) || (lpMan->m_op == StreamVmInstructionFlowMan::FLOW_VAR_OP_RANDOM ) ){
                    uint8_t op=StreamDPVmInstructions::ditINC8;

                    if ( lpMan->m_op == StreamVmInstructionFlowMan::FLOW_VAR_OP_INC ){
                        op = StreamDPVmInstructions::ditINC8 ;
                    }

                    if ( lpMan->m_op == StreamVmInstructionFlowMan::FLOW_VAR_OP_DEC ){
                        op = StreamDPVmInstructions::ditDEC8 ;
                    }

                    if ( lpMan->m_op == StreamVmInstructionFlowMan::FLOW_VAR_OP_RANDOM ){
                        op = StreamDPVmInstructions::ditRANDOM8 ;
                    }

                    StreamDPOpFlowVar8 fv8;
                    fv8.m_op = op;
                    fv8.m_flow_offset = get_var_offset(lpMan->m_var_name);
                    fv8.m_min_val     = (uint8_t)lpMan->m_min_value;
                    fv8.m_max_val     = (uint8_t)lpMan->m_max_value;
                    m_instructions.add_command(&fv8,sizeof(fv8));
                }else{
                    uint8_t op=StreamDPVmInstructions::ditINC8_STEP;

                    if ( lpMan->m_op == StreamVmInstructionFlowMan::FLOW_VAR_OP_INC ){
                        op = StreamDPVmInstructions::ditINC8_STEP ;
                    }

                    if ( lpMan->m_op == StreamVmInstructionFlowMan::FLOW_VAR_OP_DEC ){
                        op = StreamDPVmInstructions::ditDEC8_STEP ;
                    }

                    StreamDPOpFlowVar8Step fv8;
                    fv8.m_op = op;
                    fv8.m_flow_offset = get_var_offset(lpMan->m_var_name);
                    fv8.m_min_val     = (uint8_t)lpMan->m_min_value;
                    fv8.m_max_val     = (uint8_t)lpMan->m_max_value;
                    fv8.m_step        = (uint8_t)lpMan->m_step;
                    m_instructions.add_command(&fv8,sizeof(fv8));
                }
            }

            if (lpMan->m_size_bytes == 2 ){
                uint8_t op;
                if ( (lpMan->m_step == 1) || (lpMan->m_op == StreamVmInstructionFlowMan::FLOW_VAR_OP_RANDOM ) ){


                op = StreamDPVmInstructions::ditINC16;

                if ( lpMan->m_op == StreamVmInstructionFlowMan::FLOW_VAR_OP_INC ){
                    op = StreamDPVmInstructions::ditINC16 ;
                }

                if ( lpMan->m_op == StreamVmInstructionFlowMan::FLOW_VAR_OP_DEC ){
                    op = StreamDPVmInstructions::ditDEC16 ;
                }

                if ( lpMan->m_op == StreamVmInstructionFlowMan::FLOW_VAR_OP_RANDOM ){
                    op = StreamDPVmInstructions::ditRANDOM16 ;
                }

                StreamDPOpFlowVar16 fv16;
                fv16.m_op = op;
                fv16.m_flow_offset = get_var_offset(lpMan->m_var_name);
                fv16.m_min_val     = (uint16_t)lpMan->m_min_value;
                fv16.m_max_val     = (uint16_t)lpMan->m_max_value;
                m_instructions.add_command(&fv16,sizeof(fv16));
              }else{

                  op = StreamDPVmInstructions::ditINC16_STEP;

                  if ( lpMan->m_op == StreamVmInstructionFlowMan::FLOW_VAR_OP_INC ){
                      op = StreamDPVmInstructions::ditINC16_STEP ;
                  }

                  if ( lpMan->m_op == StreamVmInstructionFlowMan::FLOW_VAR_OP_DEC ){
                      op = StreamDPVmInstructions::ditDEC16_STEP ;
                  }

                  StreamDPOpFlowVar16Step fv16;
                  fv16.m_op = op;
                  fv16.m_flow_offset = get_var_offset(lpMan->m_var_name);
                  fv16.m_min_val     = (uint16_t)lpMan->m_min_value;
                  fv16.m_max_val     = (uint16_t)lpMan->m_max_value;
                  fv16.m_step        = (uint16_t)lpMan->m_step;

                  m_instructions.add_command(&fv16,sizeof(fv16));
              }
            }

            if (lpMan->m_size_bytes == 4 ){
                uint8_t op;
                if ( (lpMan->m_step == 1) || (lpMan->m_op == StreamVmInstructionFlowMan::FLOW_VAR_OP_RANDOM ) ){
                    op = StreamDPVmInstructions::ditINC32;
    
                    if ( lpMan->m_op == StreamVmInstructionFlowMan::FLOW_VAR_OP_INC ){
                        op = StreamDPVmInstructions::ditINC32 ;
                    }
    
                    if ( lpMan->m_op == StreamVmInstructionFlowMan::FLOW_VAR_OP_DEC ){
                        op = StreamDPVmInstructions::ditDEC32 ;
                    }
    
                    if ( lpMan->m_op == StreamVmInstructionFlowMan::FLOW_VAR_OP_RANDOM ){
                        op = StreamDPVmInstructions::ditRANDOM32 ;
                    }
    
                    StreamDPOpFlowVar32 fv32;
                    fv32.m_op = op;
                    fv32.m_flow_offset = get_var_offset(lpMan->m_var_name);
                    fv32.m_min_val     = (uint32_t)lpMan->m_min_value;
                    fv32.m_max_val     = (uint32_t)lpMan->m_max_value;
                    m_instructions.add_command(&fv32,sizeof(fv32));
                }else{
                    op = StreamDPVmInstructions::ditINC32_STEP;

                    if ( lpMan->m_op == StreamVmInstructionFlowMan::FLOW_VAR_OP_INC ){
                        op = StreamDPVmInstructions::ditINC32_STEP ;
                    }

                    if ( lpMan->m_op == StreamVmInstructionFlowMan::FLOW_VAR_OP_DEC ){
                        op = StreamDPVmInstructions::ditDEC32_STEP ;
                    }

                    StreamDPOpFlowVar32Step fv32;
                    fv32.m_op = op;
                    fv32.m_flow_offset = get_var_offset(lpMan->m_var_name);
                    fv32.m_min_val     = (uint32_t)lpMan->m_min_value;
                    fv32.m_max_val     = (uint32_t)lpMan->m_max_value;
                    fv32.m_step        = (uint32_t)lpMan->m_step;
                    m_instructions.add_command(&fv32,sizeof(fv32));
                }
            }


            if (lpMan->m_size_bytes == 8 ){
                uint8_t op;

                if ( (lpMan->m_step == 1) || (lpMan->m_op == StreamVmInstructionFlowMan::FLOW_VAR_OP_RANDOM ) ){

                    op = StreamDPVmInstructions::ditINC64;
    
                    if ( lpMan->m_op == StreamVmInstructionFlowMan::FLOW_VAR_OP_INC ){
                        op = StreamDPVmInstructions::ditINC64 ;
                    }
    
                    if ( lpMan->m_op == StreamVmInstructionFlowMan::FLOW_VAR_OP_DEC ){
                        op = StreamDPVmInstructions::ditDEC64 ;
                    }
    
                    if ( lpMan->m_op == StreamVmInstructionFlowMan::FLOW_VAR_OP_RANDOM ){
                        op = StreamDPVmInstructions::ditRANDOM64 ;
                    }
    
                    StreamDPOpFlowVar64 fv64;
                    fv64.m_op = op;
                    fv64.m_flow_offset = get_var_offset(lpMan->m_var_name);
                    fv64.m_min_val     = (uint64_t)lpMan->m_min_value;
                    fv64.m_max_val     = (uint64_t)lpMan->m_max_value;
                    m_instructions.add_command(&fv64,sizeof(fv64));
                }else{

                    op = StreamDPVmInstructions::ditINC64_STEP;

                    if ( lpMan->m_op == StreamVmInstructionFlowMan::FLOW_VAR_OP_INC ){
                        op = StreamDPVmInstructions::ditINC64_STEP ;
                    }

                    if ( lpMan->m_op == StreamVmInstructionFlowMan::FLOW_VAR_OP_DEC ){
                        op = StreamDPVmInstructions::ditDEC64_STEP ;
                    }

                    StreamDPOpFlowVar64Step fv64;
                    fv64.m_op = op;
                    fv64.m_flow_offset = get_var_offset(lpMan->m_var_name);
                    fv64.m_min_val     = (uint64_t)lpMan->m_min_value;
                    fv64.m_max_val     = (uint64_t)lpMan->m_max_value;
                    fv64.m_step        = (uint64_t)lpMan->m_step;
                    m_instructions.add_command(&fv64,sizeof(fv64));
                }
            }
        }

        if (ins_type == StreamVmInstruction::itPKT_WR) {
            StreamVmInstructionWriteToPkt *lpPkt =(StreamVmInstructionWriteToPkt *)inst;

            VmFlowVarRec var;
            if ( var_lookup(lpPkt->m_flow_var_name ,var) == false){

                std::stringstream ss;
                ss << "instruction id '" << ins_id << "' packet write with no valid flow varible name '" << lpPkt->m_flow_var_name << "'" ;
                err(ss.str());
            }

            if (lpPkt->m_pkt_offset + var.m_size_bytes > m_pkt_size ) {
                std::stringstream ss;
                ss << "instruction id '" << ins_id << "' packet write with packet_offset   " << lpPkt->m_pkt_offset + var.m_size_bytes  << "   bigger than packet size   "<< m_pkt_size;
                err(ss.str());
            }


            add_field_cnt(lpPkt->m_pkt_offset + var.m_size_bytes);


            uint8_t       op_size=var.m_size_bytes;
            bool is_big    = lpPkt->m_is_big_endian;
            uint8_t       flags = (is_big?StreamDPOpPktWrBase::PKT_WR_IS_BIG:0);
            uint8_t       flow_offset = get_var_offset(lpPkt->m_flow_var_name);

            if (op_size == 1) {
                StreamDPOpPktWr8 pw8;
                pw8.m_op = StreamDPVmInstructions::itPKT_WR8;
                pw8.m_flags =flags;
                pw8.m_offset =flow_offset;
                pw8.m_pkt_offset = lpPkt->m_pkt_offset;
                pw8.m_val_offset = (int8_t)lpPkt->m_add_value;
                m_instructions.add_command(&pw8,sizeof(pw8));
            }

            if (op_size == 2) {
                StreamDPOpPktWr16 pw16;
                pw16.m_op = StreamDPVmInstructions::itPKT_WR16;
                pw16.m_flags =flags;
                pw16.m_offset =flow_offset;
                pw16.m_pkt_offset = lpPkt->m_pkt_offset;
                pw16.m_val_offset = (int16_t)lpPkt->m_add_value;
                m_instructions.add_command(&pw16,sizeof(pw16));
            }

            if (op_size == 4) {
                StreamDPOpPktWr32 pw32;
                pw32.m_op = StreamDPVmInstructions::itPKT_WR32;
                pw32.m_flags =flags;
                pw32.m_offset =flow_offset;
                pw32.m_pkt_offset = lpPkt->m_pkt_offset;
                pw32.m_val_offset = (int32_t)lpPkt->m_add_value;
                m_instructions.add_command(&pw32,sizeof(pw32));
            }

            if (op_size == 8) {
                StreamDPOpPktWr64 pw64;
                pw64.m_op = StreamDPVmInstructions::itPKT_WR64;
                pw64.m_flags =flags;
                pw64.m_offset =flow_offset;
                pw64.m_pkt_offset = lpPkt->m_pkt_offset;
                pw64.m_val_offset = (int64_t)lpPkt->m_add_value;
                m_instructions.add_command(&pw64,sizeof(pw64));
            }

        }

        if (ins_type == StreamVmInstruction::itPKT_WR_MASK) {
            StreamVmInstructionWriteMaskToPkt *lpPkt =(StreamVmInstructionWriteMaskToPkt *)inst;

            VmFlowVarRec var;

            uint8_t cast_size = lpPkt->m_pkt_cast_size;
            if (!((cast_size==4)||(cast_size==2)||(cast_size==1))){
                std::stringstream ss;
                ss << "instruction id '" << ins_id << " cast size should be 1,2,4 it is "<<lpPkt->m_pkt_cast_size;
                err(ss.str());
            }

            if ( var_lookup(lpPkt->m_flow_var_name ,var) == false){

                std::stringstream ss;
                ss << "instruction id '" << ins_id << "' packet write with no valid flow varible name '" << lpPkt->m_flow_var_name << "'" ;
                err(ss.str());
            }

            if (lpPkt->m_pkt_offset + lpPkt->m_pkt_cast_size > m_pkt_size ) {
                std::stringstream ss;
                ss << "instruction id '" << ins_id << "' packet write with packet_offset   " << (lpPkt->m_pkt_offset + lpPkt->m_pkt_cast_size)  << "   bigger than packet size   "<< m_pkt_size;
                err(ss.str());
            }


            add_field_cnt(lpPkt->m_pkt_offset + lpPkt->m_pkt_cast_size);


            uint8_t       op_size = var.m_size_bytes;
            bool is_big           = lpPkt->m_is_big_endian;
            uint8_t       flags   = (is_big?StreamDPOpPktWrMask::MASK_PKT_WR_IS_BIG:0);
            uint8_t       flow_offset = get_var_offset(lpPkt->m_flow_var_name);

            /* read LSB in case of 64bit varible */
            if (op_size == 8) {
                op_size = 4;
                if ( is_big ) {
                    flow_offset +=4;
                }
            }

            StreamDPOpPktWrMask pmask;
            pmask.m_op = StreamDPVmInstructions::itPKT_WR_MASK;
            pmask.m_flags      =   flags;
            pmask.m_var_offset =   flow_offset;
            pmask.m_shift      =   lpPkt->m_shift;
            pmask.m_add_value  =   lpPkt->m_add_value;
            pmask.m_pkt_cast_size =   cast_size;
            pmask.m_flowv_cast_size = op_size;
            pmask.m_pkt_offset      = lpPkt->m_pkt_offset;
            pmask.m_mask            = lpPkt->m_mask;

            m_instructions.add_command(&pmask,sizeof(pmask));
        }


        if (ins_type == StreamVmInstruction::itFLOW_CLIENT) {
            var_cnt++;
            StreamVmInstructionFlowClient *lpMan =(StreamVmInstructionFlowClient *)inst;

            if ( lpMan->is_unlimited_flows() ){
                StreamDPOpClientsUnLimit  client_cmd;
                client_cmd.m_op =  StreamDPVmInstructions::itCLIENT_VAR_UNLIMIT;

                client_cmd.m_flow_offset = get_var_offset(lpMan->m_var_name+".ip"); /* start offset */
                client_cmd.m_flags       = 0; /* not used */
                client_cmd.m_pad         = 0;
                client_cmd.m_min_ip      = lpMan->m_client_min;
                client_cmd.m_max_ip      = lpMan->m_client_max;
                m_instructions.add_command(&client_cmd,sizeof(client_cmd));

            }else{
                StreamDPOpClientsLimit  client_cmd;
                client_cmd.m_op =  StreamDPVmInstructions::itCLIENT_VAR;

                client_cmd.m_flow_offset = get_var_offset(lpMan->m_var_name+".ip"); /* start offset */
                client_cmd.m_flags       = 0; /* not used */
                client_cmd.m_pad         = 0;
                client_cmd.m_min_port    = lpMan->m_port_min;
                client_cmd.m_max_port    = lpMan->m_port_max;
                client_cmd.m_min_ip      = lpMan->m_client_min;
                client_cmd.m_max_ip      = lpMan->m_client_max;
                client_cmd.m_limit_flows = lpMan->m_limit_num_flows;
                m_instructions.add_command(&client_cmd,sizeof(client_cmd));
            }
        }


        if (ins_type == StreamVmInstruction::itPKT_SIZE_CHANGE ) {
            StreamVmInstructionChangePktSize *lpPkt =(StreamVmInstructionChangePktSize *)inst;

            VmFlowVarRec var;
            if ( var_lookup(lpPkt->m_flow_var_name ,var) == false){

                std::stringstream ss;
                ss << "instruction id '" << ins_id << "' packet size with no valid flow varible name '" << lpPkt->m_flow_var_name << "'" ;
                err(ss.str());
            }

            if ( var.m_size_bytes != 2 ) {
                std::stringstream ss;
                ss << "instruction id '" << ins_id << "' packet size change should point to a flow varible with size 2  ";
                err(ss.str());
            }

            uint8_t       flow_offset = get_var_offset(lpPkt->m_flow_var_name);

            StreamDPOpPktSizeChange pkt_size_ch;
            pkt_size_ch.m_op =StreamDPVmInstructions::itPKT_SIZE_CHANGE;
            pkt_size_ch.m_flow_offset =  flow_offset;
            m_instructions.add_command(&pkt_size_ch,sizeof(pkt_size_ch));
        }

        ins_id++;
    }


    if ( var_cnt ==0 ){
        std::stringstream ss;
        ss << "It is not valid to have a VM program without a variable  or tuple generator ";
        err(ss.str());
    }
}